一文读懂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类,敬请期待!