共计 7732 个字符,预计需要花费 20 分钟才能阅读完成。
1. 前言
Promises/A+ 标准:
https://www.ituring.com.cn/article/66566(中文);https://promisesaplus.com/(英文);
ES6 中的 promise
采纳 Promise/A+ 标准,链接如上。
源码起源:https://repl.it/@morrain2016/Promise
本文本质上是对这份源码的一次解析。
这份源码是用 JS 代码对 promise
的模仿实现,与原生的 promise
势必存在些许的差别,然而,了解了这份源码,必定会帮忙你更深刻地了解 promise
底层原理。
2. 源码解析
本章节次要分成两个大节对源码进行解析:
- 尽可能地将源码缩减至最小,但保障了其外围性能的实现。通过这一阶段,你就深刻了解了
promise
的外围原理; - 残缺源码的其它局部详解。
2.1 外围代码
本大节分为两个局部,第一个局部为没有正文的外围代码,第二局部为具体正文的外围代码,目标很简略:读者先本人思考,若在某处卡住了可返回第二局部找到答案。
指标是:实现一个 myPromise
类,能胜利执行上面的代码:
new myPromise(res => {setTimeout(() => {console.log("test1"); // 两秒后输入 test1
res("data1");
}, 2000);
}).then((data) => {console.log(data); // 两秒后紧接着 test1 输入 data1
return new myPromise(res => {setTimeout(() => {console.log("test2"); // 三秒后输入 test2
res("data2");
}, 1000);
})
}).then(data => {console.log(data); // 三秒后紧接着 test2 输入 data2
})
2.1.1 challenge
该局部代码只保留了 promise
的两种状态:pending
和 fulfilled
,对外只暴露出 then
办法。rejected
状态和其它办法将在第二阶段解析。
无正文版:
class myPromise {callbacks = [];
state = 'pending';
value = null;
constructor(fn) {fn(this._resolve.bind(this));
}
then(onFulfilled) {return new myPromise((resolve) => {
this._handle({
onFulfilled: onFulfilled || null,
resolve: resolve,
});
});
}
_handle(callback) {if (this.state === 'pending') {this.callbacks.push(callback);
return;
}
if (!callback.onFulfilled) {callback.resolve(this.value);
return;
}
var ret = callback.onFulfilled(this.value);
callback.resolve(ret);
}
_resolve(value) {if (this.state !== 'pending') return
if (value && (typeof value === 'object' && value !== null || typeof value === 'function')) {
var then = value.then;
if (typeof then === 'function') {then.call(value, this._resolve.bind(this));
return;
}
}
this.state = 'fulfilled';
this.value = value;
this.callbacks.forEach(callback => this._handle(callback));
}
}
2.1.2 help
具体正文版:
class myPromise {callbacks = [];// resolve 执行后, 会顺次执行该数组中的回调函数
state = 'pending';// 记录 promise 的以后状态: "pending"/"fulfilled"/"rejected"
value = null;// 记录 resolve(data)中传递的数据 data
constructor(fn) {
// 立刻执行传入 promise 中的函数 fn
// 当 fn 函数中执行 resolve(), 其实是在调用外部办法:this._resolve.bind(this)
fn(this._resolve.bind(this));
}
// promise 实例的 then 办法, 将传进来的函数 onFulfilled 放入 callback 中
then(onFulfilled) {// then 办法返回一个新的 promise 实例, 以实现链式调用: promise.then().then()....
return new myPromise((resolve) => {
// 这个新 promise 实例的状态由 resolve 管制, 所以将 resolve 一并打包, 前面会用到
this._handle({
onFulfilled: onFulfilled || null,
resolve: resolve,
});
});
}
_handle(callback) {
// 顺着 then 办法的逻辑, 将 callback 对象放入存储数组中
if (this.state === 'pending') {this.callbacks.push(callback);
return;
}
// 如果 then 中没有传递参数, 即模式为 promise.then().then(cb),
// 那么第一个 then 发明的 promise 实例, 应该立刻执行 resolve, 进入 fulfilled 状态, 并执行 cb();
if (!callback.onFulfilled) {callback.resolve(this.value);// 将 value 传递上来
return;
}
/**
* promise.then(()=>{
* return 666;
* }).then(data =>{* console.log(data); // 打印出 666
* })
* 这里相当于在执行第一个 then 中的回调函数, 并将返回的 "666" 传递给下一个 then;
*
* 思考: 如果返回的不是 "666", 而是一个 promise 呢? 能够去看_resolve 办法中标记为 **(1)** 的代码逻辑
*/
var ret = callback.onFulfilled(this.value);
callback.resolve(ret);
}
_resolve(value) {// 这行代码的成果就是: new myPromise(res=>{res(); res(); res();}), 第一个 res()前面的所有 res()都会疏忽
if (this.state !== 'pending') return
// **(1)** 这一块的逻辑基于 value 是一个 promise 实例. 这一块倡议最初钻研
if (value && (typeof value === 'object' && value !== null || typeof value === 'function')) {
var then = value.then;// promise 领有 then 办法, 据此来判断该对象是不是一个 promise
if (typeof then === 'function') {
// value 是一个 promise 实例, 此时以后 promise 的状态该当依赖于 value 这个 promise 实例的状态
then.call(value, this._resolve.bind(this));
return;
// 看个例子
/**
* promise.then(()={
* return new myPromise(res => {* res(data);
* })
* })
* 这里回调函数中返回的是一个 promise 实例, 相当于下面的 value
* then.call(value, this._resolve.bind(this)), 即 value.then(this._resolve.bind(this)),
* 当 value 这个 promise 履行执行 fulfilled 后, 就会执行 this._resolve.bind(this), 也就是外层 promise 的 resolve(data)
*/
}
}
// 上面的逻辑就比较简单了
this.state = 'fulfilled';// 扭转状态
this.value = value;// 保留后果
this.callbacks.forEach(callback => this._handle(callback));// 顺次调用 callbacks 数组中的所有函数
}
}
2.2 其它局部详解
如果读者已通过第一阶段,倡议拜访该地址 https://www.ituring.com.cn/article/66566,间接浏览残缺源码,若途中遇到问题,可回到本大节来找到答案。
本大节次要从以下几个局部进行解析:
rejected
状态的补充;- 类的静态方法:resolve、reject、all、race;
- 实例的
catch
和finally
办法。
2.2.1 rejected
状态的补充
class myPromise {callbacks = [];
state = 'pending';
value = null;
constructor(fn) {
// 这里的参数多了一个_reject 函数
fn(this._resolve.bind(this), this._reject.bind(this));
}
then(onFulfilled, onRejected) {return new myPromise((resolve, reject) => {
// 这里减少了一部分代码
this._handle({
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject
});
});
}
_handle(callback) {if (this.state === 'pending') {this.callbacks.push(callback);
return;
}
// 这里和上个版本的逻辑相似, 只不过减少了 rejected 状态
let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;
if (!cb) {
cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
cb(this.value);
return;
}
/**
* cb 函数在执行过程中可能会遇到报错, 如
* promise.then(()=>{* throw Error("error!")
* })
* 这时该当执行 reject, 并将 error 信息传递进来
*/
let ret;
try {ret = cb(this.value);
cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
} catch (error) {
ret = error;
cb = callback.reject
} finally {cb(ret);
}
}
_resolve(value) {if (this.state !== 'pending') return
if (value && (typeof value === 'object' || typeof value === 'function')) {
var then = value.then;
if (typeof then === 'function') {then.call(value, this._resolve.bind(this), this._reject.bind(this));
return;
}
}
this.state = 'fulfilled';// 扭转状态
this.value = value;// 保留后果
this.callbacks.forEach(callback => this._handle(callback));
}
// 减少了_reject 外部办法
_reject(error) {if (this.state !== 'pending') return
/**
* 这里没有对 error 是不是 promise 的判断, 因而当 onRejected 函数返回一个 promise 实例时, 会间接将这个实例传递进来, 如
* new myPromise((res, rej) => {// 将一个 promise 实例传入 rej()函数中
rej(new myPromise(() => {console.log("test")}));
}).then(() => {}, data => {console.log(data); // 这里将打印出下面的 promise 实例
})
*/
this.state = 'rejected';
this.value = error;
this.callbacks.forEach(callback => this._handle(callback));
}
}
2.2.2 类的静态方法(resolve、reject、all、race)
- Promise.resolve()、Promise.reject():
// Promise.resolve()返回一个 promise 实例, 上面的代码都围绕着这一点, 只是对传进来的不同参数响应不同的行为
static resolve(value) {if (value && value instanceof Promise) {
// value 是 promise 实例, 间接返回该实例
return value;
} else if (value && typeof value === 'object' && typeof value.then === 'function') {
// value 不是 promise 实例, 然而具备 then 办法; 即它是一个 thenable 对象
let then = value.then;
return new Promise(resolve => {then(resolve);
});
} else if (value) {
// value 不是 promise 实例也不是 thenable 对象
return new Promise(resolve => resolve(value));
} else {
// value 不存在
return new Promise(resolve => resolve());
}
}
static reject(value) {if (value && typeof value === 'object' && typeof value.then === 'function') {
let then = value.then;
return new Promise((resolve, reject) => {then(reject);
});
} else {return new Promise((resolve, reject) => reject(value));
}
}
Promise.reject 与 Promise.resolve 相似,区别在于 Promise.reject 始终返回一个状态的 rejected 的 Promise 实例,而 Promise.resolve 的参数如果是一个 Promise 实例的话,返回的是参数对应的 Promise 实例,所以状态不肯定。
- Promise.all()、Promise.race()
static all(promises) {
// 返回一个 promise 实例
return new Promise((resolve, reject) => {
let fulfilledCount = 0 // 计数
const itemNum = promises.length // promises 中的 promise 个数
const rets = Array.from({length: itemNum}) // 用来保留每个 promise 的返回后果
promises.forEach((promise, index) => {Promise.resolve(promise).then(result => {
fulfilledCount++;
rets[index] = result;
if (fulfilledCount === itemNum) {resolve(rets);
}
}, reason => reject(reason));
})
})
}
static race(promises) {return new Promise(function (resolve, reject) {
// 顺次执行 promises 中的各个 promise, 只有有一个 promise 状态确定了, 就执行 resolve 或者 reject
for (let i = 0; i < promises.length; i++) {Promise.resolve(promises[i]).then(function (value) {return resolve(value)
}, function (reason) {return reject(reason)
})
}
})
}
2.2.3 实例办法(catch、finally)
// promise.catch(cb)相当于 promise.then(null, cb)
catch(onError) {return this.then(null, onError);
}
finally(onDone) {if (typeof onDone !== 'function') return this.then();
let Promise = this.constructor;
return this.then(value => Promise.resolve(onDone()).then(() => value),
reason => Promise.resolve(onDone()).then(() => { throw reason})
);
// 如果这里写成: return this.then(onDone, onDone), 会导致这样的问题:
// 1.onDone 会接管上一个 promise 传过来的参数; 2. 如果 onDone 返回一个 promise, 它将影响最终的状态;
}
3. 源码的缺点
这份源码存在一些问题,比方在执行上面这段代码时:原生 promise
的输入后果是: 2, 1,因为 then
办法中的代码是异步执行的;而这份源码实现的 promise 的输入后果是:1,2,这次要是因为以后源码的 then
办法是同步执行。
new Promise((res) => {res();
}).then(() => {console.log(1);
})
console.log(2);
4. 参考资料
图解 Promise 实现原理:https://zhuanlan.zhihu.com/p/58428287
【翻译】Promises/A+ 标准:https://www.ituring.com.cn/article/66566
源码:https://repl.it/@morrain2016/Promise