本文不解释什么是Promise,主要是用两个由浅入深的例子实现以]promise的大致功能。
先来看一下promise的简单用法:
let p = new Promise(function(resolve, reject){
resolve('成功');
}, ()=>{})
p.then((data) => {console.log(data);},(err) => {});
来写一个简易版的promise:
首先,需要创建一个构造函数promise,创建一个promise类,在使用的时候传入了一个执行器executor,executor会传入两个参数:成功(resolve)和失败(reject)。
promise的状态改变是不可逆的,开始是pending状态,成功后转为resolved状态,失败则转为rejected,这里我们用status来标识状态的变化
源码如下(后面会举一个例子分析执行过程):
class Promise {
constructor(executor) {
this.status = 'pending'; // 默认状态是等待状态
this.value = undefined; // 用来存放返回的结果值
this.errorStr = undefined; // 用来存放错误信息
this.resolveFuncs = []; //存放成功的回调
this.rejectFuncs = []; //存放失败的回调
let resolve = (data) => { // 成功调用
if (this.status === 'pending') {
this.value = data;
this.status = "resolved";
this.resolveFuncs.forEach(fn => fn());
}
}
let reject = (errorStr) => { // 失败调用
if (this.status === 'pending') {
this.errorStr = errorStr;
this.status = 'rejected';
this.rejectFuncs.forEach(fn => fn());
}
}
try {
executor(resolve, reject); // 执行
} catch (e) {
reject(e); //失败
}
}
// then方法是promise的最基本的方法,返回的是两个回调,一个成功的回调,一个失败的回调
then(onFulFilled, onRejected) {
if (this.status === 'resolved') { // 成功状态的回调
onFulFilled(this.value);
}
if (this.status === 'rejected') { // 失败状态的回调
onRejected(this.errorStr);
}
}
}
还是上面那个例子:
let p = new Promise(function(resolve, reject){
resolve('成功');
}, ()=>{})
p.then((data) => {console.log(data);},(err) => {});
执行过程分析:
- 实例化一个Promise对象,传入executor,生成constructor中的属性
- 执行resolve(‘成功’),后调用
let resolve = (data) => {
if (this.status === 'pending') {
this.value = data;
this.status = "resolved";
this.resolveFuncs.forEach(fn => fn());
}
}
将结果值存到value中,并且将状态改为resolved,再执行this.resolveFuncs中保存的方法,不过这边数组内为空。
- 执行.then(data) => {console.log(data);),调用
then(onFulFilled, onRejected) {
if (this.status === 'resolved') { // 成功状态的回调
onFulFilled(this.value);
}
if (this.status === 'rejected') { // 失败状态的回调
onRejected(this.errorStr);
}
}
将保存的value作为回调函数的参数,执行回调函数。
-
结束,返回的 p 是一个resolved状态的Promise
简易版的Promise实现看完了,现在来看一个复杂版的例子。该例子还实现了Promise的链式调用。
老样子,贴上源码,后面再举一个小题目分析一下执行过程
class Promise {
constructor(executor) {
this.status = 'pending'; // 默认状态是等待状态
this.value = ''; // 用来存放返回的结果值
this.errorStr = ''; // 用来存放错误信息
this.resolveFuncs = []; // 存放成功的回调
this.rejectFuncs = []; // 存放失败的回调
let resolve = (data) => { // 成功调用
if (this.status === 'pending') {
this.status = 'resolved';
this.value = data;
this.resolveFuncs.forEach(func => func())
}
}
let reject = (data) => { // 失败调用
if (this.status === 'pending') {
this.status = 'rejected';
this.errorStr = data;
this.rejectFuncs.forEach(func => func())
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
// 成功的回调与失败的回调
then(resolveFunc, rejectFunc) {
resolveFunc = typeof resolveFunc === 'function' ? resolveFunc : y => y;
rejectFunc = typeof rejectFunc === 'function' ? rejectFunc : err => {
throw err
};
let promise2;
if (this.status === 'resolved') {
promise2 = new Promise((resolve, reject) => {
setTimeout(() => { // 写一个宏任务
try {
let x = resolveFunc(this.value) // 执行回调
resolvePromise(promise2, x, resolve, reject) // 对结果进行处理
} catch (error) {
reject(error)
}
}, 0);
})
}
if (this.status === 'rejected') {
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = rejectFunc(this.errorStr)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
})
}
if (this.status === 'pending') {
promise2 = new Promise((resolve, reject) => {
this.resolveFuncs.push(() => { // 将成功的回调存入数组
setTimeout(() => {
try {
let x = resolveFunc(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
});
this.rejectFuncs.push(() => {
setTimeout(() => {
try {
let x = rejectFunc(this.errorStr)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
});
})
}
return promise2;
}
// catch 只传入reject的回调
catch (onRejected) {
return this.then(null, onRejected);
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) { // 不能返回自身
return reject(new TypeError('循环引用'))
}
if (x !== null && (typeof x === 'object' || typeof x === 'function')) { // 判断返回的是否为对象或者函数
try {
let then = x.then; // 取then方法
if (typeof then === 'function') { // 如果then方法存在,那么就是返回了一个promise
then.call(x, y => { // 调用then方法
resolvePromise(x, y, resolve, reject) // 对返回值递归操作,最后resolve或者reject
}, e => {
reject(e)
})
} else {
resolve(x)
}
} catch (error) {
reject(x)
}
} else {
resolve(x)
}
}
举一个例子:
let a2 = new Promise((resolve,reject) => {
resolve(2)
}).then(data => {
return new Promise((resolve,reject) => {
resolve(data+1)
})
}).then(data => {
console.log(data+1)
})
执行过程分析:
-
先实例化一个promise1对象,将executor传入,执行constructor构造函数
-
执行resolve(2)
let resolve = (data) => { // 成功调用
if (this.status === 'pending') {
this.status = 'resolved';
this.value = data;
this.resolveFuncs.forEach(func => func())
}
}
函数会将promise1的status改为resolved,将2存入value中,执行resolveFuncs数组中的方法,不过这的resolveFuncs为空
- 执行promise1.then()
then(resolveFunc, rejectFunc) {
resolveFunc = typeof resolveFunc === 'function' ? resolveFunc : y => y;
rejectFunc = typeof rejectFunc === 'function' ? rejectFunc : err => {
throw err
};
let promise2;
if (this.status === 'resolved') {
promise2 = new Promise((resolve, reject) => {
setTimeout(() => { // 写一个宏任务
try {
let x = resolveFunc(this.value) // 执行回调
resolvePromise(promise2, x, resolve, reject) // 对结果进行处理
} catch (error) {
reject(error)
}
}, 0);
})
}
(....省略.....)
return promise2;
}
因为status为resolved,则为创建一个promise2,然后写一个setTimeout加入到宏任务队列中,在同步任务和微任务都执行结束后执行,这边称它为宏任务1。宏任务1主要是将value值传入回调函数resolveFunc中,得到一个结果x。然后使用resolvePromise函数x进行分析,如果是值则直接resolve,该例子中是返回一个新的promise,所以就再传入resolvePromise函数中,递归得到结果。
这一步最后是返回promise2
在resolvePromise函数中的执行后面再分析。
- 上一步返回的promise2这边为了不混乱,称为promiseNew。执行promiseNew.then() 方法,因为promiseNew还未resolve(resolve在宏任务1中),因此状态还是pending。
执行以下代码:
then(resolveFunc, rejectFunc) {
resolveFunc = typeof resolveFunc === 'function' ? resolveFunc : y => y;
rejectFunc = typeof rejectFunc === 'function' ? rejectFunc : err => {
throw err
};
let promise2;
if (this.status === 'pending') {
promise2 = new Promise((resolve, reject) => {
this.resolveFuncs.push(() => { // 将成功的回调存入数组
setTimeout(() => {
try {
let x = resolveFunc(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
});
this.rejectFuncs.push(() => {
setTimeout(() => {
try {
let x = rejectFunc(this.errorStr)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
});
})
}
return promise2;
}
写一个方法 ()=>{} push进resolveFuncs数组中,这个方法中是一个setTimeout宏任务,这边称它为宏任务2,但是还未加入队列,因为这个方法只是存入数组,还没执行。
到这,同步任务和微任务都执行完毕,开始执行宏任务。
- 这时宏任务队列中有一个宏任务1,则执行宏任务1。
setTimeout(() => { // 写一个宏任务
try {
let x = resolveFunc(this.value) // 执行回调
resolvePromise(promise2, x, resolve, reject) // 对结果进行处理
} catch (error) {
reject(error)
}
}, 0);
根据箭头函数的this指向,这边的this.value=2。返回的x是一个resolved状态的promise,执行resolvePromise函数,该函数接受四个参数,一个是promise2,也就是promiseNew,一个是x,还有promiseNew的resolve和reject方法。在函数中会调用resolve(data+1),因此返回的x中的状态是resolved,value为3
接下来看一下resolvePromise函数
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) { // 不能返回自身
return reject(new TypeError('循环引用'))
}
if (x !== null && (typeof x === 'object' || typeof x === 'function')) { // 判断返回的是否为对象或者函数
try {
let then = x.then; // 取then方法
if (typeof then === 'function') { // 如果then方法存在,那么就是返回了一个promise
then.call(x, y => { // 调用then方法
resolvePromise(x, y, resolve, reject) // 对返回值递归操作,最后resolve或者reject
}, e => {
reject(e)
})
} else {
resolve(x)
}
} catch (error) {
reject(x)
}
} else {
resolve(x)
}
}
函数中对x与promise2进行判断,不能返回自己,否则就是循环引用。
然后判断x是否存在,是否为对象。这边的x是一个promise对象,因为会取到它的then方法,执行
then.call(x, y => { // 调用then方法
resolvePromise(x, y, resolve, reject) // 对返回值递归操作,最后resolve或者reject
}, e => {
reject(e)
})
传入x对象,与resolve和reject回调。这边执行.then方法,会再进入then中status为resolved的逻辑中,再创建一个promise2(这就是为什么我把前面的promise2改名为promiseNew),再生成一个setTimeout宏任务1-1,然后return promise2。这是再去执行宏任务1-1,当前的this.value的this指向的是x这个promise,因此value为3,传入回调函数,函数中再去执行resolvePromise,这边要注意的是,resolve和reject一直都是promiseNew传过来的。最后如果判断x为一个值,resolve(x)
- 就到了promiseNew.then()最后这一步,上一步resolve的时候会将promiseNew的状态改为resolved,并且将value设为3,再去执行resolveFuncs数组中的函数。
这时会发现,resolveFuncs数组中刚好有一个函数,就是那个包装了宏任务2的函数。
执行后,将宏任务2加入队列中。
发现以及没有同步任务与微任务了,则执行宏任务2
setTimeout(() => {
try {
let x = resolveFunc(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
会发现和宏任务1其实差不多,所以不再赘述。
不同的只是这个x并不是一个对象,而是undefind。
以上就是实现了。
参考文章: