深入浅出Promise
自 es6 推出promise以来,promise强大的异步处理功能被广大开发者认可并广泛使用,对于promise api更是驾轻就熟。但是在我们面试找工作的时候,难免会遇到一些比较难缠的面试官,可能他们提问的不仅仅是停留在api如何使用的层面上,而是:“你知道其中的实现原理吗?”,“你了解过它的底层原理吗?”,“你自己有尝试去实现过吗?” 。。。
诸如此类的灵魂拷问,而这时候的你是不是也曾像我一样,在那里支支吾吾了半天,也不清楚自己说了些啥,面试官还时不时皱了一下眉头看着你,那场面极为尴尬。
想要解决这场尴尬的场面有以下两种方案:
1.勇敢的对面试官说:“老子不懂” (这是我以前的选择)
2.硬着头皮,也开始了解相关的实现原理 (这种方法能让自己的内功慢慢变得深厚起来)
废话不多说了,下面开始来了解一下promise的原理
Promise的声明:
- 由于new Promise((resolve, reject)=>{}) ,所以传入的是一个执行器函数(executor),这个函数默认就会立即执行。
- executor里面有两个参数,一个叫resolve(成功的函数),一个叫reject(失败的函数)。
那我们就用class 类来声明
class Promise { constructor (executor) { const resolve = () => { } const reject = () => { } executor(resolve, reject); } }
Promise States:
1.Promise存在三个状态(state)pending、fulfilled、rejected
2.pending(等待态)为初始态,并可以转化为fulfilled(成功态)和rejected(失败态)
3.成功时,不可转为其他状态,且必须有一个不可改变的值(value)
4.失败时,不可转为其他状态,且必须有一个不可改变的原因(reason)
5.new Promise((resolve, reject)=>{resolve(value)}) resolve为成功,接收参数value,状态改变为fulfilled,不可再次改变。
6.new Promise((resolve, reject)=>{reject(reason)}) reject为失败,接收参数reason,状态改变为rejected,不可再次改变。
7.若是executor函数报错 直接执行reject();
1)根据 1 我们先在顶部定义三个状态
const PENDING = 'peding', FULFILLED = 'fulfilled', REJECTED = 'rejected';
2) 根据 2、3、4、5、6、7往我们的promise填充一些东西
class Promise { constructor (executor) { this.status = PENDING; this.value = undefined; this.reason = undefined; const resolve = (value) => { if (this.status === PENDING) { this.status = FULFILLED; this.value = value; } } const reject = (reason) => { if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; } } try { executor(resolve, reject); } catch (error) { reject(error); } } }
then :
- Promise有一个叫做then的方法,里面有可选两个参数:onFulfilled,onRejected,成功有成功的值,失败有失败的原因
- 当状态state为fulfilled,则执行onFulfilled,当状态state为rejected,则执行onRejected
到这里最基本的Promise算是完成了,我们来试一下这几个例子:
let promise = new Promise((reslove, reject) => { resolve("success") // reject("error") // throw new Error("Error") }) promise.then(value => { console.log(value) // success }, reason => { console.log(reason) })
let promise = new Promise((reslove, reject) => { // resolve("success") reject("error") // throw new Error("Error") }) promise.then(value => { console.log(value) }, reason => { console.log(reason) // error })
let promise = new Promise((reslove, reject) => { // resolve("success") // reject("error") throw new Error("Error") }) promise.then(value => { console.log(value) }, reason => { console.log(reason) // error })
这几个例子都能按照我们的预期走,并打印相关的信息,再看下面这个setTimeout的例子:
let promise = new Promise((reslove, reject) => { // resolve("success") // reject("error") // throw new Error("Error") setTimeout(() => { resolve("success") }, 2000) }) //没有打印 success promise.then(value => { console.log(value) }, reason => { console.log(reason) })
原本的预期应该是2s 后打印出 success,实际却没有执行,原因是在then方法中状态为pending的时候我们没有做处理,只有状态为fulfilled或者rejected的时候会执行相应的onFulfilled或onRejected,所有我们应该在状态为pending的时候,分别将成功的回调和失败的回调分别收集起来,然后在状态改变的时候再相应的去执行(这里就是用到发布/订阅的原理)。那我们先定义两个容器:onResolveCallbacks,onRejectedCallbacks
//用于收集成功的回调函数 this.onResolveCallbacks = []; //用于收集失败的回调函数 this.onRejectedCallbacks = [];
在then方法中,当状态pending的时候收集(订阅):
then(onFulfilled, onRejected) { if (this.status === FULFILLED) { onFulfilled(this.value); } if (this.status === REJECTED) { onRejected(this.reason); } if (this.status === PENDING) { // 订阅 this.onResolveCallbacks.push(() => { onFulfilled(this.value); }); this.onRejectedCallbacks.push(() => { onRejected(this.reason); }); } }
然后我们在状态改变的时候去执行(发布):
const resolve = (value) => { if (this.status === PENDING) { this.status = FULFILLED; this.value = value; // 发布 this.onResolveCallbacks.forEach((fn) => fn()); } } const reject = (reason) => { if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; // 发布 this.onRejectedCallbacks.forEach((fn) => fn()); } }
然后我们在试一下原先的例子:
let promise = new Promise((reslove, reject) => { // resolve("success") // reject("error") // throw new Error("Error") setTimeout(() => { resolve("success") }, 3000) }) promise.then(value => { console.log(value) //2s后 打印了success }, reason => { console.log(reason) })
这次能如我们所愿了,那么接下来要考虑的就是经典的链式调用了
链式调用:
在此之前我们先来看看几个例子:
1. 通过return 传递结果, 也就是说可以通过return将上一个then 的结果传递给下一个
let promise = new Promise((reslove, reject) => { resolve("success") }) promise.then(value => { return value }, reason => { console.log(reason) }) .then(value => { console.log(value) //success })
2.通过新的promise resolve 结果
promise.then(value => { return value }, reason => { console.log(reason) }) .then(value => { return new Promise((reslove, reject) => { resolve("success") }) }) .then(value => { console.log(value) //success })
promise.then(value => { return value }, reason => { console.log(reason) }) .then(value => { return new Promise((reslove, reject) => { setTimeout(() => { resolve("success") },2000) }) }) .then(value => { console.log(value) //success })
4.通过新的promise reject 结果
promise.then(value => { return value }, reason => { console.log(reason) }) .then(value => { return new Promise((reslove, reject) => { setTimeout(() => { reject("error") },2000) }) }) .then(value => { console.log(value) }, reason => { console.log(reason) //error })
5.then 走了失败的回调后 再走then
promise.then(value => { return value }, reason => { console.log(reason) }) .then(value => { return new Promise((reslove, reject) => { setTimeout(() => { reject("error") },2000) }) }) .then(value => { console.log("FulFilled:"+ value) }, reason => { console.log("Rejected:"+ reason) //Rejected: error // 默认 return undefined }) .then(value => { console.log("FulFilled:"+ value) //FulFilled: undefined }, reason => { console.log("Rejected:"+ reason) })
6.then 直接 throw new Error
promise.then(value => { return value }, reason => { console.log(reason) }) .then(value => { return new Promise((reslove, reject) => { setTimeout(() => { reject("error") },2000) }) }) .then(value => { console.log("FulFilled:"+ value) }, reason => { console.log("Rejected:"+ reason) //Rejected: error // 默认 return undefined }) .then(value => { throw new Error("throw new Error") }) .then(value => { console.log( value) }, reason => { console.log("抛出异常:"+ reason) // 抛出异常: throw new Error })
7.用catch 捕获异常 (在本质上就是一个then, 遵循then的运行原则)
promise.then(value => { return value }, reason => { console.log(reason) }) .then(value => { return new Promise((reslove, reject) => { setTimeout(() => { reject("error") },2000) }) }) .then(value => { console.log("FulFilled:"+ value) }, reason => { console.log("Rejected:"+ reason) //Rejected: error // 默认 return undefined }) .then(value => { throw new Error("throw new Error") }) .then(value => { console.log( value) } // 走最近的失败的回调函数 // , reason => { // console.log("抛出异常:"+ reason) // 抛出异常: throw new Error // } ) .catch((err) => { console.log("catch: " + err) // atch: throw new Error return "catch error" }) .then(value => { console.log("then:"+ value) //then: catch error })
promise.then(value => { return promise //TypeError: Chaining cycle detected for promise #<MyPromise> 循环引用报错 })
总结一下:
成功的条件是什么?
1.then return 普通值value
2.then return 新的promise 成功态的结果 value
失败的条件是什么
1.then return 新的promise 失败态的原因 reason
let promise2 = promise.then(value => { //return 第一次返回的新的Promise结果 }).then(value => { // 第二次返回的结果 }) let promise2 = promise.then(value => { //retun 第一次返回的新的Promise结果 }) // 第一次then返回的新的Promise结果 promise2.then(value => { })
这两种写法也是不一样的。
了解这些情况以后,参照Promises/A+规范来实现:
1、为了达成链式,我们默认在第一个then里返回一个promise。规范中规定了一种方法,就是在then里面返回一个新的promise,称为promise2: promise2 = new Promise((resolve, reject)=>{})
将这个promise2返回的值传递到下一个then中
如果返回一个普通的值,则将普通的值传递给下一个then中
2、当我们在第一个then中 return 了一个参数(参数未知,需判断)。这个return出来的新的promise就是onFulfilled()或onRejected()的值
规范中规定onFulfilled()或onRejected()的值,即第一个then返回的值,叫做x,判断x的函数叫做resolvePromise
let promise2 = new Promise((resolve, reject) => { if (this.status === FULFILLED) { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } if (this.status === REJECTED) { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } if (this.status === PENDING) { // 订阅 this.onResolveCallbacks.push(() => { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); }); this.onRejectedCallbacks.push(() => { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); }); } }); return promise2; }
实现resolvePromise函数:
1、判断x
Otherwise, if x is an object or function,Let then be x.then
x 不能是null
x 是普通值 直接resolve(x)
x 是对象或者函数(包括promise), let then = x.then
2、当x是对象或者函数(默认promise)
声明了then
如果取then报错,则走reject()
如果then是个函数,则用call执行then,第一个参数是this,后面是成功的回调和失败的回调
如果成功的回调还是pormise,就递归继续解析
3、成功和失败只能调用一个 所以设定一个called来防止多次调用
function resolvePromise(promise2, x, resolve, reject){ // 循环引用报错 if(x === promise2){ // reject报错 return reject(new TypeError('Chaining cycle detected for promise')); } // 防止多次调用 let called; // x不是null 且x是对象或者函数 if (x != null && (typeof x === 'object' || typeof x === 'function')) { try { // A+规定,声明then = x的then方法 let then = x.then; // 如果then是函数,就默认是promise了 if (typeof then === 'function') { // 就让then执行 第一个参数是this 后面是成功的回调 和 失败的回调 then.call(x, y => { // 成功和失败只能调用一个 if (called) return; called = true; // resolve的结果依旧是promise 那就继续解析 resolvePromise(promise2, y, resolve, reject); }, err => { // 成功和失败只能调用一个 if (called) return; called = true; reject(err);// 失败了就失败了 }) } else { resolve(x); // 直接成功即可 } } catch (e) { // 也属于失败 if (called) return; called = true; // 取then出错了那就不要在继续执行了 reject(e); } } else { resolve(x); } }
但是到这里还会有问题:
// x 普通值 promise then(onFulfilled, onRejected) { // 在下面的这段代码没执行完之前 resolvePromise 能拿到promise2吗? 很显然是不能的 let promise2 = new Promise((resolve, reject) => { if (this.status === FULFILLED) { let x = onFulfilled(this.value); // 拿不到promise2 resolvePromise(promise2, x, resolve, reject); } if (this.status === REJECTED) { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } if (this.status === PENDING) { // 订阅 this.onResolveCallbacks.push(() => { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); }); this.onRejectedCallbacks.push(() => { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); }); } }); return promise2; }
那我们用 setTimeout 异步执行是不是就可以解决了,结合一下Promises/A+ 规范:
1、Promises/A+规范规定onFulfilled,onRejected都是可选参数,如果他们不是函数,必须被忽略
onFulfilled返回一个普通的值,成功时直接等于 value => value
onRejected返回一个普通的值,失败时如果直接等于 value => value,则会跑到下一个then中的onFulfilled中,所以直接扔出一个错误 reason => throw err
2、Promises/A+规范规定onFulfilled或onRejected不能同步被调 用,必须异步调用。我们就用setTimeout解决异步问题
如果onFulfilled或onRejected报错,则直接返回reject()
// x 普通值 promise then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }; let promise2 = new MyPromise((resolve, reject) => { if (this.status === FULFILLED) { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); } if (this.status === REJECTED) { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); } if (this.status === PENDING) { // 订阅 this.onResolveCallbacks.push(() => { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }); this.onRejectedCallbacks.push(() => { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }); } }); return promise2; }
这些终于舒服了
catch 方法:
catch (errorCallback) { return this.then(null, errorCallback); }
resolve 方法:
Promise.resolve = function(val){ return new Promise((resolve,reject)=>{ resolve(val) }); }
reject 方法:
Promise.reject = function(val){ return new Promise((resolve,reject)=>{ reject(val) }); }
race 方法:
Promise.race = function(promises){ return new Promise((resolve,reject)=>{ for(let i=0;i<promises.length;i++){ promises[i].then(resolve,reject) }; }) }
all 方法:获取所有的promise,都执行then,把结果放到数组,一起返回
Promise.all = function(promises){ let arr = []; let i = 0; function processData(index,data){ arr[index] = data; i++; if(i == promises.length){ resolve(arr); }; }; return new Promise((resolve,reject)=>{ for(let i=0;i<promises.length;i++){ promises[i].then(data=>{ processData(i,data); },reject); }; });
finally 方法:
Promise.prototype.finally = function(onFinished) { return this.then(val => { onFinished() return val }).catch((err) => { onFinished() return err }) }
any 方法:
跟 Promise.all 正好相反,只要有一个是 promise 是 fulfilled 时,则直接返回该结果,如果都是 rejected ,则报错 。
Promise .any([ Promise.reject('rejected'), Promise.resolve('fulfilled') ]) .then(res => console.log(res)) .catch(err => console.error(err)); // fulfilled Promise .any([ Promise.reject('rejected1'), Promise.reject('rejected2') ]) .then(res => console.log(res)) .catch(err => console.error(err)); // AggregateError: All promises were rejected Promise .any([ Promise.resolve('resolve1'), Promise.resolve('resolve1') ]) .then(res => console.log(res)) .catch(err => console.error(err)); // resolve1

浙公网安备 33010602011771号