在说 Promise 之前我们先简单说一下什么是同步异步?
同步(Sync): 所谓同步, 就是发出一个功能调用时, 在没有得到结果之前, 该调用就不返回或继续执行后续操作.
异步(Async): 异步与同步相对, 当一个异步过程调用发出后, 调用者在没有得到结果之前, 就可以继续执行后续操作. 当这个调用完成后, 一般通过状态, 通知和回调来通知调用者.
Promise 是什么?
Promise 是 JS 异步编程中的重要概念, 异步抽象处理对象, 是目前比较流行 JavaScript 异步编程解决方案之一. 这句话说的很明白了, Promise 是一种用于解决异步问题的思路, 方案或者对象方式.
在 JS 中, 经常使用异步的地方是 Ajax 交互. 比如在 es5 时代, jQuery 的 Ajax 的使用 success 来完成异步的:
- $.Ajax({
- url:'/xxx',
- success:()=>{},
- error: ()=>{}
- })
这种方法可以清楚的让读代码的人明白那一部分是 Ajax 请求成功的回调函数和失败的回调函数. 但是问题来了, 当一次请求需要连续请求多个接口时, 这段代码仿佛进入了一团乱麻中:
- // 第一次
- $.Ajax({
- url:'/xxx',
- success:()=>{
- // 第二次
- $.Ajax({
- url:'/xxx',
- success:()=>{
- // 第三次
- $.Ajax({
- url:'/xxx',
- success:()=>{
- // 可能还会有
- },
- error: ()=>{}
- })
- },
- error: ()=>{}
- })
- },
- error: ()=>{}
- })
也许因为 success 和 error 这两个函数的存在, 理解这段代码会很简单, 但是当我们更改需求的时候, 这将成为一个棘手的问题. 这就是回调地狱.
当然, 这是 es5 时代. 当 JS 这门语言发展到 es6 时代时, Promise 的出现给异步带来了变革. Promise 提供一个 then, 来为异步提供回调函数:
- $.Ajax({
- url:'/xxx',
- }).then( ()=>{
- // 成功的回调
- }, ()=>{
- // 失败的回调
- })
而其先进之处则是, 可以在 then 方法中继续写 Promise 对象并返回, 然后继续调用 then 来进行回调操作.
Promise 的特点?
对象的状态不会受到外界的影响. Promise 代表的是一个异步的操作. 一共有三种状态 pending(进行中),resolved(已做完)和 rejected(已失败), 异步操作的结果, 决定这 Promise 最终的状态. 成功的则会使用 resolved 作为回调, 如果失败则会使用 rejected 作为回调, 任何的操作都无法改变其中的状态.
Promise 的状态一旦改变, 就永远不会再改变. Promise 的改变只有两种情况, Pending -> rejected 或者 Pending-> resolved,Promise 的最大特点也就是在此, 如果你错过了他的返回结果, 如果再想通过某种方法, 去获得 Promise 的返回结果是获取不到的.
简单来说, Promise 就是用同步的方式写异步的代码, 用来解决回调问题
then()方法
then 方法就是把原来的回调写法分离出来, 在异步操作执行完后, 用链式调用的方式执行回调函数.
而 Promise 的优势就在于这个链式调用. 我们可以在 then 方法中继续写 Promise 对象并返回, 然后继续调用 then 来进行回调操作.
下面做一个吃早饭的演示, 它们是层层依赖的关系, 下一步的的操作需要使用上一部操作的结果.(这里使用 setTimeout 模拟异步操作), 正式开发可以用 Ajax 异步
- function cake() {
- console.log('排队买早饭')
- let breakfast = new Promise((resolve, reject) => {
- setTimeout(() => {
- console.log('买了手抓饼')
- resolve('手抓饼')
- }, 100)
- })
- return breakfast
- }
- function eat(data) {
- console.log('开始吃早饭:' + data)
- let eatbreakfast = new Promise((resolve, reject) => {
- setTimeout(() => {
- console.log('吃完早饭')
- resolve('去上班')
- }, 100)
- });
- return eatbreakfast
- }
- cake().then((res) => {
- return eat(res)
- }).then((res) => {
- console.log(res)
- })
打印结果:
catch()方法:
catch 在链式写法中可以捕获前面 then 中发送的异常.
- function cake() {
- console.log('排队买早饭')
- let breakfast = new Promise((resolve, reject) => {
- setTimeout(() => {
- console.log('没买到饼')
- reject('饼买光了')
- }, 100)
- })
- return breakfast
- }
- function eat(data) {
- console.log('开始吃早饭:' + data)
- let eatbreakfast = new Promise((resolve, reject) => {
- setTimeout(() => {
- console.log('吃完早饭')
- resolve('去上班')
- }, 100)
- });
- return eatbreakfast
- }
- cake().then((res) => {
- return eat(res)
- }).catch((res) => {
- console.log(res)
- })
打印结果:
它的另一个作用是, 当执行 resolve 的回调 (也就是上面 then 中的第一个参数) 时, 如果抛出异常了(代码出错了), 那么也不会报错卡死 JS, 而是会进到这个 catch 方法中.
- function cake() {
- console.log('排队买早饭')
- let breakfast = new Promise((resolve, reject) => {
- setTimeout(() => {
- console.log('买了豆浆')
- resolve('豆浆')
- }, 100)
- })
- return breakfast
- }
- function eat(data) {
- console.log('开始吃早饭:' + data)
- let eatbreakfast = new Promise((resolve, reject) => {
- setTimeout(() => {
- console.log('吃完早饭')
- resolve('去上班')
- }, 100)
- });
- return eatbreakfast
- }
- cake().then((res) => {
- throw new Error("买了坏的豆浆");
- return eat(res)
- }).catch((res) => {
- console.log(res)
- })
打印结果:
all()方法:
Promise 的 all 方法提供了并行执行异步操作的能力, 并且在所有异步操作执行完后才执行回调.
比如下面代码, 两个个异步操作是并行执行的, 等到它们都执行完后才会进到 then 里面. 同时 all 会把所有异步操作的结果放进一个数组中传给 then.
- function eat() {
- console.log('排队买手抓饼')
- let breakfast = new Promise((resolve, reject) => {
- setTimeout(() => {
- console.log('刚做好的手抓饼')
- resolve('热乎的手抓饼')
- }, 100)
- })
- return breakfast
- }
- function drink() {
- console.log('排队买豆浆')
- let soybean = new Promise((resolve, reject) => {
- setTimeout(() => {
- console.log('刚打磨的豆浆')
- resolve('热乎的豆浆')
- }, 100)
- })
- return soybean
- }
- Promise.all([eat(), drink()]).then((results) =>{
- console.log("早饭都买好了")
- console.log(results)
- })
打印结果:
race()方法:
race 按字面解释, 就是赛跑的意思. race 的用法与 all 一样, 只不过 all 是等所有异步操作都执行完毕后才执行 then 回调. 而 race 的话只要有一个异步操作执行完毕, 就立刻执行 then 回调.
注意: 其它没有执行完毕的异步操作仍然会继续执行, 而不是停止.
这里我们将上面样例的 all 改成 race
- function eat() {
- console.log('排队买手抓饼')
- let breakfast = new Promise((resolve, reject) => {
- setTimeout(() => {
- console.log('刚做好的手抓饼')
- resolve('热乎的手抓饼')
- }, 100)
- })
- return breakfast
- }
- function drink() {
- console.log('排队买豆浆')
- let soybean = new Promise((resolve, reject) => {
- setTimeout(() => {
- console.log('刚打磨的豆浆')
- resolve('热乎的豆浆')
- }, 100)
- })
- return soybean
- }
- Promise.race([eat(), drink()]).then((results) =>{
- console.log("哈哈, 早饭都买好了")
- console.log(results)
- })
打印结果:
来源: http://www.bubuko.com/infodetail-2985410.html