原文链接史上最最最详细的手写Promise教程
我们工作中免不了运用promise用来解决异步回调问题。平时用的很多库或者插件都运用了promise 例如axios、fetch等等。但是你知道promise是咋写出来的呢?
别怕~这里有本promisesA+规范,便宜点10元卖给你了。
1、Promise 的声明
首先呢,promise肯定是一个类,我们就用class来声明。
-
由于
new Promise((resolve, reject)=>{}),所以传入一个参数(函数),秘籍里叫他executor,传入就执行。 -
executor里面有两个参数,一个叫resolve(成功),一个叫reject(失败)。
-
由于resolve和reject可执行,所以都是函数,我们用let声明。
classPromise{ // 构造器constructor(executor){ // 成功let resolve = () => { }; // 失败let reject = () => { }; // 立即执行 executor(resolve, reject); } }
解决基本状态
秘籍对Promise有规定:
Promise存在三个状态(state)pending、fulfilled、rejected
pending(等待态)为初始态,并可以转化为fulfilled(成功态)和rejected(失败态)
成功时,不可转为其他状态,且必须有一个不可改变的值(value)
失败时,不可转为其他状态,且必须有一个不可改变的原因(reason)
new Promise((resolve, reject)=>{resolve(value)}) resolve为成功,接收参数value,状态改变为fulfilled,不可再次改变。
new Promise((resolve, reject)=>{reject(reason)}) reject为失败,接收参数reason,状态改变为rejected,不可再次改变。
若是executor函数报错 直接执行reject();
于是乎,我们获得以下代码
classPromise{
constructor(executor){
// 初始化state为等待态this.state = 'pending';
// 成功的值this.value = undefined;
// 失败的原因this.reason = undefined;
let resolve = value => {
// state改变,resolve调用就会失败if (this.state === 'pending') {
// resolve调用后,state转化为成功态this.state = 'fulfilled';
// 储存成功的值this.value = value;
}
};
let reject = reason => {
// state改变,reject调用就会失败if (this.state === 'pending') {
// reject调用后,state转化为失败态this.state = 'rejected';
// 储存失败的原因this.reason = reason;
}
};
// 如果executor执行报错,直接执行rejecttry{
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
}
then方法
秘籍规定:Promise有一个叫做then的方法,里面有两个参数:onFulfilled,onRejected,成功有成功的值,失败有失败的原因
-
当状态state为fulfilled,则执行onFulfilled,传入this.value。当状态state为rejected,则执行onRejected,传入this.value
-
onFulfilled,onRejected如果他们是函数,则必须分别在fulfilled,rejected后被调用,value或reason依次作为他们的第一个参数
classPromise{ constructor(executor){...} // then 方法 有两个参数onFulfilled onRejected then(onFulfilled,onRejected) { // 状态为fulfilled,执行onFulfilled,传入成功的值if (this.state === 'fulfilled') { onFulfilled(this.value); }; // 状态为rejected,执行onRejected,传入失败的原因if (this.state === 'rejected') { onRejected(this.reason); }; } }
这下武学初成,可以对付对付江湖小杂毛了,但是对于带setTimeout的江洋大盗还是没辙。
解决异步实现
现在基本可以实现简单的同步代码,但是当resolve在setTomeout内执行,then时state还是pending等待状态 我们就需要在then调用的时候,将成功和失败存到各自的数组,一旦reject或者resolve,就调用它们
类似于发布订阅,先将then里面的两个函数储存起来,由于一个promise可以有多个then,所以存在同一个数组内。
// 多个then的情况let p = newPromise();
p.then();
p.then();
成功或者失败时,forEach调用它们
classPromise{
constructor(executor){
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
// 成功存放的数组this.onResolvedCallbacks = [];
// 失败存放法数组this.onRejectedCallbacks = [];
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
// 一旦resolve执行,调用成功数组的函数this.onResolvedCallbacks.forEach(fn=>fn());
}
};
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
// 一旦reject执行,调用失败数组的函数this.onRejectedCallbacks.forEach(fn=>fn());
}
};
try{
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled,onRejected) {
if (this.state === 'fulfilled') {
onFulfilled(this.value);
};
if (this.state === 'rejected') {
onRejected(this.reason);
};
// 当状态state为pending时if (this.state === 'pending') {
// onFulfilled传入到成功数组this.onResolvedCallbacks.push(()=>{
onFulfilled(this.value);
})
// onRejected传入到失败数组this.onRejectedCallbacks.push(()=>{
onRejected(this.value);
})
}
}
}
解决链式调用
我门常常用到new Promise().then().then(),这就是链式调用,用来解决回调地狱
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
-
首先,要看x是不是promise。
-
如果是promise,则取它的结果,作为新的promise2成功的结果
-
如果是普通值,直接作为promise2成功的结果
-
所以要比较x和promise2
-
resolvePromise的参数有promise2(默认返回的promise)、x(我们自己
return的对象)、resolve、reject -
resolve和reject是promise2的
classPromise{ constructor(executor){ this.state = 'pending'; this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; let resolve = value => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; this.onResolvedCallbacks.forEach(fn=>fn()); } }; let reject = reason => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; this.onRejectedCallbacks.forEach(fn=>fn()); } }; try{ executor(resolve, reject); } catch (err) { reject(err); } } then(onFulfilled,onRejected) { // 声明返回的promise2let promise2 = newPromise((resolve, reject)=>{ if (this.state === 'fulfilled') { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); }; if (this.state === 'rejected') { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); }; if (this.state === 'pending') { this.onResolvedCallbacks.push(()=>{ let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); }) this.onRejectedCallbacks.push(()=>{ let x = onRejected(this.value); resolvePromise(promise2, x, resolve, reject); }) } }); // 返回promise,完成链式return promise2; } }
完成resolvePromise函数
秘籍规定了一段代码,让不同的promise代码互相套用,叫做resolvePromise
-
如果 x === promise2,则是会造成循环引用,自己等待自己完成,则报“循环引用”错误
let p = newPromise(resolve => { resolve(0); }); var p2 = p.then(data => { // 循环引用,自己等待自己完成,一辈子完不成return p2; })
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.then2、当x是对象或者函数(默认promise) -
声明了then
-
如果取then报错,则走reject()
-
如果then是个函数,则用call执行then,第一个参数是this,后面是成功的回调和失败的回调
-
如果成功的回调还是pormise,就递归继续解析 3、成功和失败只能调用一个 所以设定一个called来防止多次调用
functionresolvePromise(promise2, x, resolve, reject){ // 循环引用报错if(x === promise2){ // reject报错return reject(newTypeError('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); } }
解决其他问题
1、秘籍规定onFulfilled,onRejected都是可选参数,如果他们不是函数,必须被忽略
-
onFulfilled返回一个普通的值,成功时直接等于
value => value -
onRejected返回一个普通的值,失败时如果直接等于 value => value,则会跑到下一个then中的onFulfilled中,所以直接扔出一个错误
reason => throw err2、秘籍规定onFulfilled或onRejected不能同步被调用,必须异步调用。我们就用setTimeout解决异步问题 -
如果onFulfilled或onRejected报错,则直接返回reject()
classPromise{ constructor(executor){ this.state = 'pending'; this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; let resolve = value => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; this.onResolvedCallbacks.forEach(fn=>fn()); } }; let reject = reason => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; this.onRejectedCallbacks.forEach(fn=>fn()); } }; try{ executor(resolve, reject); } catch (err) { reject(err); } } then(onFulfilled,onRejected) { // onFulfilled如果不是函数,就忽略onFulfilled,直接返回value onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; // onRejected如果不是函数,就忽略onRejected,直接扔出错误 onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }; let promise2 = newPromise((resolve, reject) => { if (this.state === 'fulfilled') { // 异步 setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }; if (this.state === 'rejected') { // 异步 setTimeout(() => { // 如果报错try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }; if (this.state === 'pending') { this.onResolvedCallbacks.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) }); }; }); // 返回promise,完成链式return promise2; } }
大功告成
如何验证我们的promise是否正确
1、先在后面加上下述代码
2、npm 有一个promises-aplus-tests插件 npm i promises-aplus-tests -g 可以全局安装 mac用户最前面加上sudo
3、命令行 promises-aplus-tests [js文件名] 即可验证
// 目前是通过他测试 他会测试一个对象// 语法糖Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = newPromise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
module.exports = Promise;
//npm install promises-aplus-tests 用来测试自己的promise 符不符合promisesA+规范