一文读懂Promise

什么是Promise

$.ajax({    success: (res) => {        $.ajax({            success: (res) => {                $.ajax({                    success: (res) => {                        //...                    }                })            }        })    }})

这就是典型的回调地狱,不仅代码臃肿,可读性差,而且耦合度过高,不易维护。代码无法复用,还容易隐藏bug。

Promise规范的出现就是为了解决这种问题。

这里强调一下,Promise是一种解决方案,它是一种规范。

ES6原生提供了Promise对象,在日常开发中经常会接触到Promise相关操作,本文将介绍Promise的使用方法及相关原理,希望能有所帮助。

Promise有三种状态:

  • Pending(等待态)
  • Fulfilled(成功态)
  • Rejected(失败态)

Promise的特点是状态只能由Pending转换为Fulfilled或Rejected,并且一旦改变不再更改。

new Promise((resolve,reject) => {    /*executor*/})

Promise是一个带有resolve,reject两个参数的构造函数(executor)。这个构造函数在Promise创建的时候立即执行,resolve和reject两个函数在被调用时,分别将Promise的状态更改为fulfilled(成功态)和rejected(失败态),并传递成功的值或失败的原因。executor内部通常会执行一些异步操作,当异步操作执行完毕时,可根据执行结果相应地调用resolve或reject(可能成功可能失败)。如果executor抛出一个错误,那么该Promise也将转为失败态。

Promise原型上还具有then和catch方法,调用后返回一个Promise实例,因此可以被链式调用。

每一个Promise实例都带有一个then方法,该方法有两个参数(onFulfilled,onRejected),分别为Promise成功和失败时的回调。

let p = new Promise((resolve,reject) => {    resolve('success');})p.then(value => {    console.log(value);        //success}).then(value => {    console.log(value);        //没有返回值时,undefined})

当返回了一个值或者一个成功态的Promise时,then返回的Promise都是成功态,若没有返回值时也是成功态,而回调函数的参数值为undefind

当抛出一个错误或者返回一个失败态Promise,then返回的Promise都是失败态。

let p = new Promise((resolve,reject) => {    reject('fail');})p.then(() => {    },err => {    console.log(err);        //fail});
then方法传递的成功/失败函数,这两个函数可以返回一个promise;如果返回的是一个promise,则该promise状态将作为下一次then的结果。
let p = new Promise((resolve,reject) => {    resolve('success')});let p2 = new Promise((resolve,reject) => {    resolve('success2')})p.then(value => {    return p2;                //p2的成功态将作为下一次then的状态}).then(value => {    console.log(value)})

Promise.prototype.catch方法返回也是一个Promise,使用方式同then方法,用于处理异常捕获情况。该方法捕获属于就近捕获原则,离错误最近的一个catch将会捕获错误并进行处理,后续将不再捕获。

若失败状态被then方法中失败函数回调触发,则catch方法将不进行捕获。
let p1 = new Promise((resolve,reject) => {    resolve('success');});p1.then(value => {    throw 'a new error';}).catch(e => {    console.log(e);        //a new error}).then(() => {    console.log('after a catch the chain is restored');})

Promise.all方法返回一个新的Promise对象,在参数对象中所有的promise对象都成功时才会触发成功态,一旦有任意一个对象失败则立即触发该Promise对象的失败态。当触发成功态后,会把一个包含所有Promise返回值的数组按顺序返回。如果触犯失败态,则会把第一个失败的Promise对象错误信息返回。

let p1 = new Promise((resolve,reject) => {    setTimeout(() => {        resolve('p1')    },0)});let p2 = new Promise((resolve,reject) => {    setTimeout(() => {        resolve('p2')    },0)});let promise = Promise.all([p1,p2]).then(value => {    console.log(value);        //['p1','p2']})

Promise.race方法是把参数中任意第一个完成状态转换的Promise作为成功或失败状态,并返回该Promise对象。

let p1 = new Promise((resolve,reject) => {    setTimeout(() => {        resolve('p1')    },1000)});let p2 = new Promise((resolve,reject) => {    setTimeout(() => {        reject('p2')    },0)});Promise.race([p1,p2]).then((value) => {    console.log(value)},(err) => {    console.log(err);        //p2})

Promise.resolve(value)返回一个成功态,并且将value传递给对应的then方法;

Promise.reject(reason)返回一个失败态,并且将reason传递个对应的then方法。

Promise的优缺点

优点缺点
解决回调无法监测进行状态
链式调用新建立即执行且无法取消
减少嵌套内部错误无法抛出

Promise练习(Node环境)

1.等待状态

const promise1 = new Promise((resolve, reject) => {    setTimeout(() => {        resolve('success')    }, 1000)})const promise2 = promise1.then(() => {    return 'an error'})console.log('promise1', promise1)console.log('promise2', promise2)setTimeout(() => {    console.log('promise1', promise1)    console.log('promise2', promise2)}, 2000)

2.状态改变

const promise = new Promise((resolve, reject) => {    resolve('success1')    reject('error')    resolve('success2')})promise.then((res) => {    console.log('then: ', res)}).catch((err) => {    console.log('catch: ', err)})

3.链式调用

Promise.resolve(1)    .then((res) => {        console.log(res)        return 2    })    .catch((err) => {        return 3    })    .then((res) => {        console.log(res)    })

4.链式调用

const promise = new Promise((resolve, reject) => {    setTimeout(() => {        console.log('executor')        resolve('success')    }, 1000)})const start = Date.now()promise.then((res) => {    console.log(res, Date.now() - start)})promise.then((res) => {    console.log(res, Date.now() - start)})//Promise的构造函数只执行一次,并且立即执行;promise的then方法可以被多次调用,每次都返回新的promise实例。
const promise = new Promise((resolve, reject) => {    setTimeout(() => {        console.log('executor')        resolve('success')    }, 1000)})const promise2 = promise.then(res => {    console.log(res);})promise2.then(res => {    console.log(res)})

5.错误捕获

Promise.resolve()    .then(() => {        return new Error('error!!!')    })    .then((res) => {        console.log('then: ', res)    })    .catch((err) => {        console.log('catch: ', err)    })
Promise.resolve()    .then(() => {        throw new Error('error');        //return Promise.reject('error')    })    .then((res) => {        console.log('then: ', res)    })    .catch((err) => {        console.log('catch: ', err)    })
then或catch方法返回一个错误对象并不会被捕获,只有在抛出错误或返回失败态时才会引起捕获。

6.引用错误

const promise = Promise.resolve()  .then(() => {    return promise  })promise.catch(e => {    console.log(e)})//[TypeError: Chaining cycle detected for promise #<Promise>]
返回值不能是promise实例本身,因为此时实例并未构建完成,造成死循环,因此抛出类型错误。

7.事件环

process.nextTick(() => {    console.log('nextTick')})Promise.resolve()    .then(() => {        console.log('then')    })setImmediate(() => {    console.log('setImmediate')})console.log('end')

8.穿透

Promise.resolve(1)    .then(2)    .then(Promise.resolve(3))    .then(console.log)
then和catch方法的参数是一个函数,若非函数则发生穿透

Promise A+规范

规范:https://promisesaplus.com/

该网站介绍了若需要实现Promise需要实现的规范方法。当多个库实现自己的Promise类时,为了能实现相应的效果,该规范提供了实现标准,并提供了promises-aplus-tests测试包测试实现能力。

后续将推出文章介绍如何实现自己的Promise类,敬请期待!