Promise简介
背景
我们都知道Promise是用来代替回调函数解决异步问题。在Promise出现之前,回调函数用法为:
document.addEventListener('click',callback)
上述的事件绑定非常常见,理解起来也没有任何问题,实际上现在也非常常用,但是如果是下面这种情况呢:
async(1, function(value){
async(value, function(value){
async(value, function(value){
async(value, function(value){
async(value, function(value){
async(value, final);
});
});
});
});
});
当一个异步调用依赖另一个异步调用的回调函数时,就会出现回调嵌套,一旦这种依赖层级过深,就会出现所谓的 ”回调地狱“。Promise的出现,可以很好的解决这个问题。
Promise表示承诺、许诺的意思,意思是使用了Promise之后他肯定会给我们答复,无论成功或者失败都会给我们一个答复,在《Javascript高级程序设计》第四版中,正式将Promise翻译成”期约“,表示约定未来发生的事,这个翻译从一定程度上解释了Promise的本意。
Promise
规范有很多,如Promise/A
,Promise/B
,Promise/D
以及 Promise/A
的升级版 Promise/A+
。ES6
中采用了 Promise/A+
规范。
Pomise/A+规范
- 一个promise必须有3个状态,pending,fulfilled(resolved),rejected。pending表示进行中,resolved表示完成,rejected表示失败,当处于pending状态的时候,可以转移到fulfilled(resolved)或者rejected状态。当处于fulfilled(resolved)状态或者rejected状态的时候,就不可变。promise英文译为承诺,也就是说promise的状态一旦发生改变,就永远是不可逆的。
(2)一个promise必须有一个then方法,then方法接受两个参数:
promise.then(onFulfilled,onRejected)
其中onFulfilled方法表示状态从pending——>fulfilled(resolved)时所执行的方法,而onRejected表示状态从pending——>rejected所执行的方法。
(3)为了实现链式调用,then方法必须返回一个promise,并且then方法可以被同一个 promise
调用多次。(即注册多个回调函数)
API
1.构造函数
function Promise(resolver) {}
2.原型方法
Promise.prototype.then = function() {}
Promise.prototype.catch = function() {}
3.静态方法
Promise.resolve = function() {}
Promise.reject = function() {}
Promise.all = function() {}
Promise.race = function() {}
Pomise使用
promise使用非常简单,通过new操作创建Promise对象,Promise构造函数接收一个executor
立即执行函数,该函数接收两个参数:resolve和reject。resolve函数被调用时会将pending状态转化成fullfilled状态,然后触发then
方法中的第一个回调;同理,reject函数会将pending状态转化成rejected状态,然后触发then
方法中的第二回调,或者catch回调。
const promise = new Promise(function(resolve, reject) {
//待处理的异步逻辑
//处理结束后,调用resolve或reject方法
})
promise.then(result => {
/* 这是thenable函数, 如果当前的Promise已经是resolved状态, 该函数会立即执行,如果当前是未决状态, 则会加入作业队列, 等待Promise状态变为resolved会立即执行 */
},
err => {
/* 这是catchable函数, 如果当前的Promise已经是rejected状态, 该函数会立即执行, 如果当前是未决状态, 则会加入作业队列, 等待Promise状态变为rejected会立即执行 */
})
下面是一个简单的例子:
var myPromise1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log('----async1 start');
resolve('async1 resolved');
},100)
})
myPromise1.then((res)=>{
console.log(res);
console.log('----async1 end')
},
(err)=>{
console.log(err)
console.log('----async1 end')
})
then可以用来注册thenable函数和catchable函数,而catch只能注册catchable函数。
**resolve方法的参数除了正常的值以外,还可能是另一个Promise实例。**例如,p1和p2都是Promise的实例,但是p2的resolve方法将p1作为参数,这时p1的状态就会传递给p2。如果调用的时候,p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是fulfilled或者rejected,那么p2的回调函数将会立刻执行。
链式调用
正由于前面所说,Promise.prototype.then方法返回的是一个新的Promise对象,因此可以采用链式写法。
promise1
.then(function(json) {
return json.name;
})
.then(function(name) {
// proceed
});
第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。
但是如果前一个回调函数返回的是Promise对象,这时后一个回调函数就会等待该Promise对象有了运行结果,才会进一步调用。
错误捕获
Promise.prototype.catch方法是Promise.prototype.then(null, rejection)的别名,用于指定发生错误时的回调函数。
//reject 捕获new Promise((resolve,reject)=>{ throw new Error('error');})
.then(()=>{ console.log('resolve'); },
()=>{ console.log('reject'); })
.catch(()=>{ console.log('catch');});
//catch捕获new Promise((resolve,reject)=>{ reject('error');})
.then(()=>{ console.log('resolve');})
.catch(()=>{ console.log('catch');});
throw和reject都可被fail和catch方法捕捉,主要区别是throw不能用在异步中,而reject可以。
Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
Promise.all
const promise = Promise.all([p1, p2, p3]);
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
该方法在处理多个异步请求时非常有用,例如一个页面需要请求多个资源,只有在所有请求都完成后才会显示。
Promise.race
const promise = Promise.race([p1, p2, p3]);
Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。不同的是,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。率先改变的 Promise 实例的返回值,作为参数传递给p的回调函数。这也是为什么叫race(赛跑)。
Promise.resolve(args)
该方法返回一个resolved状态的promise,参数作为状态数据。
Promise.reject(args)
该方法返回一个rejected状态的promise,参数作为状态数据。
总结
-
`Promise`的状态一经改变就不能再改变。
-
`.then`和`.catch`都会返回一个新的`Promise`。
-
`catch`不管被连接到哪里,都能捕获上层的错误。
-
在`Promise`中,返回任意一个非 `promise` 的值都会被包裹成 `promise` 对象,例如`return 2`会被包装为`return Promise.resolve(2)`。
-
`Promise` 的 `.then` 或者 `.catch` 可以被调用多次, 当如果`Promise`内部的状态一经改变,并且有了一个值,那么后续每次调用`.then`或者`.catch`的时候都会直接拿到该值。
-
`.then` 或者 `.catch` 中 `return` 一个 `error` 对象并不会抛出错误,所以不会被后续的 `.catch`捕获。
-
`.then` 或 `.catch` 返回的值不能是 promise 本身,否则会造成死循环。
-
`.then` 或者 `.catch` 的参数期望是函数,传入非函数则会发生值穿透。
-
`.then`方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候你可以认为`catch`是`.then`第二个参数的简便写法。
-
`.finally`方法也是返回一个`Promise`,他在`Promise`结束的时候,无论结果为`resolved`还是`rejected`,都会执行里面的回调函数。
Promise实现
var Promise = (function() {
function Promise(resolver) {
if (typeof resolver !== 'function') { //resolver必须是函数
throw new TypeError('Promise resolver ' + resolver + ' is not a function')
}
if (!(this instanceof Promise)) return new Promise(resolver)
var self = this //保存this
self.callbacks = [] //保存onResolve和onReject函数集合
self.status = 'pending' //当前状态
function resolve(value) {
setTimeout(function() { //异步调用
if (self.status !== 'pending') {
return
}
self.status = 'resolved' //修改状态
self.data = value
for (var i = 0; i < self.callbacks.length; i++) {
self.callbacks[i].onResolved(value)
}
})
}
function reject(reason) {
setTimeout(function(){ //异步调用
if (self.status !== 'pending') {
return
}
self.status = 'rejected' //修改状态
self.data = reason
for (var i = 0; i < self.callbacks.length; i++) {
self.callbacks[i].onRejected(reason)
}
})
}
try{
resolver(resolve, reject) //执行resolver函数
} catch(e) {
reject(e)
}
}
function resolvePromise(promise, x, resolve, reject) {
var then
var thenCalledOrThrow = false
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise!'))
}
if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) {
try {
then = x.then
if (typeof then === 'function') {
then.call(x, function rs(y) {
if (thenCalledOrThrow) return
thenCalledOrThrow = true
return resolvePromise(promise, y, resolve, reject)
}, function rj(r) {
if (thenCalledOrThrow) return
thenCalledOrThrow = true
return reject(r)
})
} else {
return resolve(x)
}
} catch(e) {
if (thenCalledOrThrow) return
thenCalledOrThrow = true
return reject(e)
}
} else {
return resolve(x)
}
}
Promise.prototype.then = function(onResolved, onRejected) {
//健壮性处理,处理点击穿透
onResolved = typeof onResolved === 'function' ? onResolved : function(v){return v}
onRejected = typeof onRejected === 'function' ? onRejected : function(r){throw r}
var self = this
var promise2
//promise状态为resolved
if (self.status === 'resolved') {
return promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
try {
//调用then方法的onResolved回调
var x = onResolved(self.data)
//根据x的值修改promise2的状态
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
//promise2状态变为rejected
return reject(e)
}
})
})
}
//promise状态为rejected
if (self.status === 'rejected') {
return promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
try {
//调用then方法的onReject回调
var x = onRejected(self.data)
//根据x的值修改promise2的状态
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
//promise2状态变为rejected
return reject(e)
}
})
})
}
//promise状态为pending
//需要等待promise的状态改变
if (self.status === 'pending') {
return promise2 = new Promise(function(resolve, reject) {
self.callbacks.push({
onResolved: function(value) {
try {
//调用then方法的onResolved回调
var x = onResolved(value)
//根据x的值修改promise2的状态
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
//promise2状态变为rejected
return reject(e)
}
},
onRejected: function(reason) {
try {
//调用then方法的onResolved回调
var x = onRejected(reason)
//根据x的值修改promise2的状态
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
//promise2状态变为rejected
return reject(e)
}
}
})
})
}
}
//获取当前Promise传递的值
Promise.prototype.valueOf = function() {
return this.data
}
//由then方法实现catch方法
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected)
}
//finally方法
Promise.prototype.finally = function(fn) {
return this.then(function(v){
setTimeout(fn)
return v
}, function(r){
setTimeout(fn)
throw r
})
}
Promise.prototype.spread = function(fn, onRejected) {
return this.then(function(values) {
return fn.apply(null, values)
}, onRejected)
}
Promise.prototype.inject = function(fn, onRejected) {
return this.then(function(v) {
return fn.apply(null, fn.toString().match(/\((.*?)\)/)[1].split(',').map(function(key){
return v[key];
}))
}, onRejected)
}
Promise.prototype.delay = function(duration) {
return this.then(function(value) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(value)
}, duration)
})
}, function(reason) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject(reason)
}, duration)
})
})
}
Promise.all = function(promises) {
return new Promise(function(resolve, reject) {
var resolvedCounter = 0
var promiseNum = promises.length
var resolvedValues = new Array(promiseNum)
for (var i = 0; i < promiseNum; i++) {
(function(i) {
Promise.resolve(promises[i]).then(function(value) {
resolvedCounter++
resolvedValues[i] = value
if (resolvedCounter == promiseNum) {
return resolve(resolvedValues)
}
}, function(reason) {
return reject(reason)
})
})(i)
}
})
}
Promise.race = function(promises) {
return new Promise(function(resolve, reject) {
for (var i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(function(value) {
return resolve(value)
}, function(reason) {
return reject(reason)
})
}
})
}
Promise.resolve = function(value) {
var promise = new Promise(function(resolve, reject) {
resolvePromise(promise, value, resolve, reject)
})
return promise
}
Promise.reject = function(reason) {
return new Promise(function(resolve, reject) {
reject(reason)
})
}
Promise.fcall = function(fn){
// 虽然fn可以接收到上一层then里传来的参数,但是其实是undefined,所以跟没有是一样的,因为resolve没参数啊
return Promise.resolve().then(fn)
}
Promise.done = Promise.stop = function(){
return new Promise(function(){})
}
Promise.deferred = Promise.defer = function() {
var dfd = {}
dfd.promise = new Promise(function(resolve, reject) {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
try { // CommonJS compliance
module.exports = Promise
} catch(e) {}
return Promise
})()
参考: