一文读懂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+规范
该网站介绍了若需要实现Promise需要实现的规范方法。当多个库实现自己的Promise类时,为了能实现相应的效果,该规范提供了实现标准,并提供了promises-aplus-tests
测试包测试实现能力。
后续将推出文章介绍如何实现自己的Promise类,敬请期待!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。