promise
的手写版,平时业务几乎用不到,但手写会对加深对promise
的理解,本文侧重理解核心的异步链式调用原理。
这边在看了最简实现 Promise,支持异步链式调用(20 行),试着再重新手写一次。
同样,这个 Promise 的实现不考虑任何异常情况,只考虑代码最简短,从而便于理解核心的异步链式调用原理。
executor 立即执行
promise 的本质是一个对象,由构造函数Promise
产生实例。
产生实例的时候,需要传个executor
函数,此函数直接执行。
先看看最简单的 promise,可以丢在控制台:
new Promise(() => {
// 这句立即执行
console.log(2);
});
复制代码
仅仅实现这种程度的话,其实超简单:
function Promise(executor) {
executor();
}
复制代码
resolve 延时执行
但一般不会这么用的,executor
至少也得有个resolve
。
而resolve
之后,会执行这个promise
的所有then
函数,而then
这个函数,参数也是一个函数,这个函数将拿到resolve
的值。
var p1 = new Promise((resolve) => {
resolve(2);
});
p1.then((value) => {
console.log('then1', value); // 2
});
p1.then((value) => {
// 2
console.log('then2', value); // 2
});
复制代码
这里有个典型的发布-订阅
模式,then
其实是订阅,resolve
是发布。
这种模式的通病:用数组装函数,订阅的本质就是将函数放进数组,发布的本质就是数组的函数挨个执行。
then
每个实例上面都有,所以是定义在原型链上面的。
于是:
function Promise(executor) {
// 数组收集函数
this.cbs = [];
// resolve执行的时候,就是数组里的函数挨个执行
const resolve = (value) => {
// 但是这里是空的
console.log(this.cbs);
this.cbs.forEach((fn) => fn(value));
};
executor(resolve);
}
Promise.prototype.then = function (onFulfill) {
// 订阅就是数组里增加函数
this.cbs.push(onFulfill);
};
复制代码
但是这样是不行滴,书写顺序上resolve
先执行,then
后执行,所以resolve
执行的时候,cbs
是空的。
怎么样让then
先执行,之后再让resolve
执行呢?
书写的先后顺序肯定不能改了,那想想别的法子让执行顺序发生变化,最容易想到的就是resolve
延时执行。
const resolve = (value) => {
setTimeout(() => {
this.cbs.forEach((fn) => fn(value));
});
};
复制代码
再运行,就没有问题了!
then 的链式调用
then
函数,其实可以链式调用的。
var p1 = new Promise((resolve) => {
resolve(2);
});
var p2 = p1.then((value) => {
console.log('then2', value);
});
// 注意这里换成了p2,链式调用的体现
p2.then(() => {
console.log('链式调用的then');
});
p2.then(() => {
console.log('链式调用的then2');
});
复制代码
所以then
实际上返回的是一个新的promise
。
因为then
是一个新的promise
,所以要想后面的then
能执行,内部一定会有resolve
这里特别注意!then
本身是个函数,这个函数的参数是另外一个函数。promise
是then
这个函数返回的,而和另外一个函数无关!
Promise.prototype.then = function (onFulfill) {
this.cbs.push(onFulfill);
// then肯定返回promise
return new Promise((resolve) => {
resolve();
});
};
复制代码
难点:then 的链式调用 拿到 前一个then的参数函数返回值
then
函数,除了链式调用,还可以拿到前个then
的参数函数返回值,注意是参数函数的返回值,而不是 then
本身的返回值,then
本身肯定是返回promise
的。
var p1 = new Promise((resolve) => {
resolve(2);
});
var p2 = p1.then((value) => {
console.log('then2', value);
return 3;
});
// 注意这里换成了p2,链式调用的体现
p2.then((value) => {
console.log('链式调用的then', value); // 3
});
p2.then((value) => {
console.log('链式调用的then2', value); // 3
});
// p1有1个回调函数
console.log(p1)
// p2有2个回调函数
console.log(p2)
复制代码
then
能执行的前提是,对应的promise
执行了resolve
,这样能拿到resolve
的值。
所以后面的 then
能拿到的前提是:前面的 then
(返回值是promise
) 将参数函数返回值resolve
了。这里面略绕,得好好想想。
综上实现下:
Promise.prototype.then = function (onFulfill) {
// then肯定返回promise
return new Promise((resolve) => {
// 这里面略绕,resolve需要函数的返回值,但是函数只能在cbs里执行,所以加工了onFulfill
const fn = (value) => {
const returnValue = onFulfill(value);
// 为了让后面的then能执行,这里添加resolve
resolve(returnValue);
};
// 这里注意!push的fn是加工后的onFulfill,fn执行之后,resolve才能执行
this.cbs.push(fn);
});
};
复制代码
then 的参数函数返回 promise
then
的参数函数,如果返回 promise
(userPromise) 的话,必须解析完 promise
之后的结果再传递到下一个 then
。
var p1 = new Promise((resolve) => {
resolve(2);
});
var p2 = p1.then((value) => {
console.log('then2', value);
return new Promise((resolve) => resolve(3));
});
// 注意这里换成了p2,链式调用的体现
p2.then((value) => {
console.log('链式调用的then', value); // 3
});
p2.then((value) => {
console.log('链式调用的then2', value); // 3
});
复制代码
想拿到 promise
的解析结果,其实直接在后面调用 then
即可。
// 稍微判断下返回值
const fn = (value) => {
const returnValue = onFulfill(value);
// 返回值是不是promise
if (returnValue instanceof Promise) {
const userPromise = returnValue;
// 是的话,将解析完之后的结果resolve
userPromise.then(resolve);
} else {
resolve(returnValue);
}
};
复制代码
终版的最核心的 promise 就是
function Promise(executor) {
// 数组收集函数
this.cbs = [];
// resolve执行的时候,就是数组里的函数挨个执行
const resolve = (value) => {
setTimeout(() => {
this.cbs.forEach((fn) => fn(value));
});
};
executor(resolve);
}
Promise.prototype.then = function (onFulfill) {
// then肯定返回promise
return new Promise((resolve) => {
// 这里面略绕,resolve需要函数的返回值,但是函数只能在cbs里执行,所以加工了onFulfill
const fn = (value) => {
const returnValue = onFulfill(value);
// 为了让后面的then能执行,这里添加resolve
if (returnValue instanceof Promise) {
const userPromise = returnValue;
userPromise.then(resolve);
} else {
resolve(returnValue);
}
};
// 这里注意!push的fn是加工后的onFulfill
this.cbs.push(fn);
});
};
复制代码
总结
当然promise
本身有其状态和值,还有很多边界情况,暂时没做考虑,后期再慢慢研究吧。
resolve
执行其实就是then
的参数函数执行。
同样,想知道promise
的解析结果,也在then
里才能拿到。
then
本身是个函数,挂载在原型链上,里面的this
是当前的promise
实例,且返回新的promise
实例
而其参数是个函数,这个函数是在resolve
之后执行的,这个函数的返回值是由用户决定的。