promise原理
promise其实是对回调函数做了封装,本质上没有什么新的东西
是构造函数,包含一个执行器executor,在promise内部,有三种状态pending、fulfilled和rejected,一旦内部状态发生变化,就不可更改
构造函数的实例对象方法有:
- Promise.prototype.then
- Promise.prototype.catch
- Promise.prototype.finally
构造函数的静态方法有:
- Promise.resolve
- Promise.reject
- Promise.all
- Promise.race
- Promise.any
- Promise.allSettled
下面就让我们带着问题一步一步的去实现promise源码吧~
实现一个最简单的promise
首先我们要知道promise的用法,这样才能根据参数用法去进行代码编写
const p1 = new MyPromise((resolve, reject) => {
resolve("p1");
});
p1.then(
res => {
console.log(res);
},
err => {
console.log(err);
}
);
这里MyPromise是一个构造函数,构造函数中的代码是同步执行的,这个构造函数接收两个实参resolve和reject,这两个参数是函数需要在构造函数中内部定义,当调用resolve函数时候,会把当前的promise状态改变成fulfilled,而执行reject函数时则改为rejected,二者相互对立,只执行其中一个,另外一个并不会继续执行,简单的MyPromise第一步代码如下:
const isFunction = value => typeof value === "function";
const isObject = value => typeof value === "object" && value !== null;
const PENDING = "pending",
FULFILLED = "fulfilled",
REJECTED = "rejected";
class MyPromise {
constructor(executor) {
this.state = PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = value => {
if (this.state === PENDING) {
this.state = FULFILLED;
this.value = value;
}
};
const reject = reason => {
if (this.state === PENDING) {
this.state = REJECTED;
this.reason = reason;
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
}
上面为了方面判断函数和对象类型,就直接定义了两个函数,构造函数的中执行器executor执行的错误需要在try...catch里捕获,错误被reject出去
然而resolve后的结果只能被创建的promise实例的then方法接收到,then方法接收两个形参(onFulfilled,onRejected),在这两个回调函数里可以接受到promise的resolve或reject的值,then方法的第一版如下:
then(onFulfilled, onRejected) {
onFulfilled = isFunction(onFulfilled) ? onFulfilled : value => value;
onRejected = isFunction(onRejected)
? onRejected
: reason => {
throw reason;
};
if (this.state === FULFILLED) {
onFulfilled(this.value);
}
if (this.state === REJECTED) {
onRejected(this.reason);
}
}
根据MDN的promise中then方法的描述:
所以对两个回调函数参数做了处理
这个时候在对上述代码执行,则返回结果:
const p1 = new MyPromise((resolve, reject) => {
resolve("p1");
});
p1.then(
res => {
console.log(res); // 输出p1
},
err => {
console.log(err);
}
);
如果构造函数里有setTimeout等异步任务呢
const p1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("p1");
}, 1000);
});
p1.then(
res => {
console.log(res);
},
err => {
console.log(err);
}
);
如上代码,如果在执行器内有异步任务,则执行p1.then时候的this.state的状态是pending,那此时then里的onFulfilled代码则不会被执行
则此时就需要有两个缓存来暂时的存储这些回调函数,等resolve或reject状态发生变化了再依次执行
构造函数里:
class MyPromise{
constructor(executor){
...
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = value => {
if (this.state === PENDING) {
this.state = FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
const reject = reason => {
if (this.state === PENDING) {
this.state = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
....
}
...
}
then方法里:
then(onFulfilled, onRejected) {
onFulfilled = isFunction(onFulfilled) ? onFulfilled : value => value;
onRejected = isFunction(onRejected)
? onRejected
: reason => {
throw reason;
};
if (this.state === FULFILLED) {
onFulfilled(this.value);
}
if (this.state === REJECTED) {
onRejected(this.reason);
}
if (this.state === PENDING) {
this.onFulfilledCallbacks.push(() => {
onFulfilled(this.value);
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason);
});
}
}
以上代码加完后,上面setTimeout例子就会延迟1秒输出p1
then的返回值是什么,链式调用呢
const p1 = new MyPromise((resolve, reject) => {
resolve("p1");
});
const p2 = p1.then(
res => {
console.log(res); // 输出p1
},
err => {
console.log(err);
}
);
p2.then(res => { // 报错
console.log(res);
});
then方法的返回值根据MDN介绍:
所以它会返回一个新的promise,其中的状态会根据上述情况而定then方法里:
...
const p = new MyPromise((resolve, reject) => {
let x;
if (this.state === FULFILLED) {
x = onFulfilled(this.value);
resolve(x);
}
if (this.state === REJECTED) {
x = onRejected(this.reason);
reject(x);
}
if (this.state === PENDING) {
this.onFulfilledCallbacks.push(() => {
x = onFulfilled(this.value);
resolve(x);
});
this.onRejectedCallbacks.push(() => {
x = onRejected(this.reason);
reject(x);
});
}
});
return p;
...
创建一个promise实例,再返回,则前面的例子代码会打印出:
但是then其实是一个微任务,应该在当前同步代码完成之后在执行,给上面例子代码最后加一行
const p1 = new MyPromise((resolve, reject) => {
resolve("p1");
});
const p2 = p1.then(
res => {
console.log(res);
return "p2";
},
err => {
console.log(err);
}
);
p2.then(res => {
console.log(res);
});
console.log("end");
则输出是p1->p2->end,这其实是不对的,then是微任务,所以这里用queueMicrotask()包裹一层即可:
...
const p = new MyPromise((resolve, reject) => {
let x;
if (this.state === FULFILLED) {
queueMicrotask(() => {
try {
x = onFulfilled(this.value);
resolve(x);
} catch (err) {
reject(err);
}
});
}
if (this.state === REJECTED) {
queueMicrotask(() => {
try {
x = onRejected(this.reason);
resolve(x);
} catch (err) {
reject(err);
}
});
}
if (this.state === PENDING) {
this.onFulfilledCallbacks.push(() => {
queueMicrotask(() => {
try {
x = onFulfilled(this.value);
resolve(x);
} catch (err) {
reject(err);
}
});
});
this.onRejectedCallbacks.push(() => {
queueMicrotask(() => {
try {
x = onRejected(this.reason);
resolve(x);
} catch (err) {
reject(err);
}
});
});
}
});
return p;
...
此时上述例子则输出正确:
如果resolve(new MyPromise())呢
这里相当于resolve个promise了
const p1 = new MyPromise((resolve, reject) => {
resolve(
new MyPromise((resolve, reject) => {
resolve("p1 promise");
})
);
});
const p2 = p1.then(
res => {
console.log(res);
return "p2";
},
err => {
console.log(err);
}
);
此时输出会是直接一个promise对象信息
这肯定是不对的,我们期望输出的是p1 promise吗,这里涉及到了状态的递归沿用问题,即resolve里的promise状态作为当前的状态,代码如下:
then(onFulfilled, onRejected) {
onFulfilled = isFunction(onFulfilled) ? onFulfilled : value => value;
onRejected = isFunction(onRejected)
? onRejected
: reason => {
throw reason;
};
const p = new MyPromise((resolve, reject) => {
let x;
if (this.state === FULFILLED) {
queueMicrotask(() => {
try {
x = onFulfilled(this.value);
resolvePromise(p, x, resolve, reject);
} catch (err) {
reject(err);
}
});
}
if (this.state === REJECTED) {
queueMicrotask(() => {
try {
x = onRejected(this.reason);
resolvePromise(p, x, resolve, reject);
} catch (err) {
reject(err);
}
});
}
if (this.state === PENDING) {
this.onFulfilledCallbacks.push(() => {
queueMicrotask(() => {
try {
x = onFulfilled(this.value);
resolvePromise(p, x, resolve, reject);
} catch (err) {
reject(err);
}
});
});
this.onRejectedCallbacks.push(() => {
queueMicrotask(() => {
try {
x = onRejected(this.reason);
resolvePromise(p, x, resolve, reject);
} catch (err) {
reject(err);
}
});
});
}
});
return p;
}
其中resolvePromise是判断resolve()里参数还是一个promise的作用,而且不能循环引用,具体代码如下:
function resolvePromise(p, x, resolve, reject) {
let called = false;
if (p === x) {
reject(new TypeError("引用错误"));
}
if (isObject(x) || isFunction(x)) {
try {
const then = x.then;
if (isFunction(then)) {
then.call(
x,
y => {
if (called) return;
called = true;
resolvePromise(p, y, resolve, reject);
},
r => {
if (called) return;
called = true;
reject(r);
}
);
} else {
if (called) return;
called = true;
resolve(x);
}
} catch (err) {
if (called) return;
called = true;
reject(err);
}
} else {
resolve(x);
}
}
只要一个resolve后,其状态就确定了,不能在使用reject,所以用一个called标记下。
完整的代码如下:
const isFunction = value => typeof value === "function";
const isObject = value => typeof value === "object" && value !== null;
const PENDING = "pending",
FULFILLED = "fulfilled",
REJECTED = "rejected";
class MyPromise {
constructor(executor) {
this.state = PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = value => {
if (this.state === PENDING) {
this.state = FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
const 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 = isFunction(onFulfilled) ? onFulfilled : value => value;
onRejected = isFunction(onRejected)
? onRejected
: reason => {
throw reason;
};
const p = new MyPromise((resolve, reject) => {
let x;
if (this.state === FULFILLED) {
queueMicrotask(() => {
try {
x = onFulfilled(this.value);
resolvePromise(p, x, resolve, reject);
} catch (err) {
reject(err);
}
});
}
if (this.state === REJECTED) {
queueMicrotask(() => {
try {
x = onRejected(this.reason);
resolvePromise(p, x, resolve, reject);
} catch (err) {
reject(err);
}
});
}
if (this.state === PENDING) {
this.onFulfilledCallbacks.push(() => {
queueMicrotask(() => {
try {
x = onFulfilled(this.value);
resolvePromise(p, x, resolve, reject);
} catch (err) {
reject(err);
}
});
});
this.onRejectedCallbacks.push(() => {
queueMicrotask(() => {
try {
x = onRejected(this.reason);
resolvePromise(p, x, resolve, reject);
} catch (err) {
reject(err);
}
});
});
}
});
return p;
}
}
function resolvePromise(p, x, resolve, reject) {
let called = false;
if (p === x) {
reject(new TypeError("引用错误"));
}
if (isObject(x) || isFunction(x)) {
try {
const then = x.then;
if (isFunction(then)) {
then.call(
x,
y => {
if (called) return;
called = true;
resolvePromise(p, y, resolve, reject);
},
r => {
if (called) return;
called = true;
reject(r);
}
);
} else {
if (called) return;
called = true;
resolve(x);
}
} catch (err) {
if (called) return;
called = true;
reject(err);
}
} else {
resolve(x);
}
}
最后用专门的脚本测试上述写的代码是否符合promise A+规范,在代码文件的末尾加上:
MyPromise.defer = MyPromise.deferred = function () {
let dfd = {};
dfd.promise = new MyPromise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};
module.exports = MyPromise;
然后全局安装promises-aplus-tests工具包,使用promises-aplus-tests index.js命令即可测试:
本文中写的源码全部通过~
还有一些promise的API如下:
Promise.prototype.catch返回一个Promise.resolve()包裹的一个新的promise
相当于Promise.prototype.then(null, onRejected)
catch(onRejected) {
return this.then(null, onRejected);
}
Promise.prototype.finally返回一个新的promise
这个方法的回调函数不管成功还是失败都会执行
finally(onFinally) {
return this.then(
value => MyPromise.resolve(onFinally()).then(() => value),
error =>
MyPromise.resolve(onFinally()).then(() => {
throw error;
})
);
}
Promise.resolve返回一个以给定值解析的promise对象
- 如果这个参数值是一个promise,则直接返回这个promise
- 如果是个thenable对象(带有then方法的)返回的promise会“跟随”这个thenable的对象,采用它的最终状态
- 否则返回的promise将以此值完成
- 不能是自身,因为是无线循环
MyPromise.resolve = function (value) {
if (value instanceof MyPromise) {
return value;
}
return new MyPromise((resolve, reject) => {
if (isObject(value) && isFunction(value.then)) {
queueMicrotask(() => {
value.then(resolve, reject);
});
} else {
resolve(value);
}
});
};
Promise.reject返回一个带有拒绝理由的promise
MyPromise.reject = function (reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
};
Promise.all接收一个promise的可迭代对象,返回一个promise实例
根据MDN规则:
MyPromise.all = function (promises) {
return new MyPromise((resolve, reject) => {
if (
!promises ||
!promises[Symbol.iterator] ||
typeof promises[Symbol.iterator] !== "function"
) {
reject(new TypeError("promises不是可迭代对象"));
} else {
promises = Array.from(promises);
const result = [];
let count = 0;
if (!promises.length) {
resolve([]);
} else {
for (let i = 0; i < promises.length; i++) {
MyPromise.resolve(promises[i])
.then(value => {
result[i] = value;
count++;
if (count === promises.length) {
resolve(result);
}
})
.catch(err => {
reject(err);
});
}
}
}
});
};
Promise.race接收一个promises可迭代对象,返回一个promise实例
MDN中的规则:
MyPromise.race = function (promises) {
return new MyPromise((resolve, reject) => {
if (
!promises ||
!promises[Symbol.iterator] ||
typeof promises[Symbol.iterator] !== "function"
) {
reject(new TypeError("参数不是Iterator类型"));
} else {
for (const promise of promises) {
MyPromise.resolve(promise)
.then(value => {
resolve(value);
})
.catch(err => {
reject(err);
});
}
}
});
}
最后遗留一个问题,我自己也迷惑,如果有知道解决方法的小伙伴欢迎留言
如果在构造函数中resolve(new Promise())的源码该怎么写:
const p1 = new Promise((resolve, reject) => {
resolve(
new Promise((resolve, reject) => {
resolve("resolve promise");
})
);
});
const p2 = p1.then(
res => {
console.log(res); // 输出resolve promise
},
err => {
console.log(err);
}
);
遗留问题找到了解决方法
要实现以上问题,核心的resolve()里面的应该返回这个promise的then函数结果,与之前的resolvePromise中的递归类似但又些许差别,代码如下:
...
const resolve = value => {
// 判断value是否是thenable对象
if (isObject(value)) {
try {
const then = value.then;
if (isFunction(then)) {
return then.call(value, resolve, reject);
}
} catch (err) {
return reject(err);
}
}
if (this.state === PENDING) {
this.state = FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
...
// reject函数保持不变
上一个问题的输出结果: