是什么
Promise是一种对异步操作的封装,主流的规范是Promise/A+。
Promise可以使得异步代码层次清晰,便于理解,且更加容易维护。
追求的效果
那么我们的Promise将要实现怎样的效果呢?举个简单的例子如下:
function p1() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(1);
}, 1000);
});
}
function p2(value) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(2 + value);
}, 1000);
});
}
p1().then(function(res) {
console.log(res); // 1000ms后输出1
return Promise.resolve(res); // 显式的return一个Promise对象
}).then(p2).then(function(res) {
console.log(res); // 再过1000ms后输出3
});
我们手写的Promise最终也可以实现这个效果
最基本的构建
function Promise(fn) {
let value = null; // 异步函数执行后的结果
let deferred; // 异步函数执行后,真正要执行的回调函数
this.then = function(onFulfilled) {
deferred = onFulfilled;
}
function resolve(newValue) {
value = newValue;
deferred(value);
}
fn(resolve);
}
代码很少,也很容易理解:
- 创建Promise实例的参数是fn,并将其内部的resolve方法作为参数传递给异步函数
- Promise的then方法用于注册回调函数,即赋值给内部的deferred
- 当异步函数回调成功后,会将结果作为参数来执行resolve,而实际上是执行deferred函数
- 这样就实现了在恰当的时机(异步函数回调成功后),执行恰当的回调(then注册的回调方法)
改进1
目前的代码只能注册一个回调方法,这显然不符合我们的预期,所以将内部的deferred修改为deferreds数组,相应的执行resolve时,也要遍历deferreds数组依次执行:
function Promise(fn) {
let value = null;
let deferreds = [];
this.then = function(onFulfilled) {
deferreds.push(onFulfilled);
}
function resolve(newValue) {
value = newValue;
deferreds.forEach((deferred) => {
deferred(value);
});
}
fn(resolve);
}
改进2
实现then的链式调用,非常简单:
this.then = function(onFulfilled) {
deferreds.push(onFulfilled);
return this;
}
这样就可以实现:
p1().then(function(res) {
// do sth. with res
}).then(function(res) {
// do sth. else with res
});
延时resolve
目前的Promise有一个bug,假如fn中所包含的是同步代码,则resolve会立即执行,此时then还没有注册回调函数,内部的deferreds为空数组,所以回调函数不会如预期一样执行。
所以,为resolve添加一个延时:
function resolve(newValue) {
value = newValue;
setTimeout(() => {
deferreds.forEach((deferred) => {
deferred(value);
});
}, 0);
}
以上保证了resolve于then注册回调函数之后执行。
引入状态
目前还存在一点问题,现在用then注册回调函数的行为都是在异步操作成功之前,一旦异步操作已经成功后,内部resolve已经执行完毕,再用then方法注册回调函数就不会再执行了。
想要解决这个问题,需要引入规范中的三个状态:pending、fulfilled、rejected,它们之间的关系是:
引入状态后的代码:
function Promise(fn) {
let state = "pending";
let value = null;
let deferreds = [];
this.then = function(onFulfilled) {
// state若为pending则将onFulfilled加入队列
if(state === "pending") {
deferreds.push(onFulfilled);
return this;
}
// state若为fulfilled则立即执行onFulfilled
onFulfilled(value);
return this;
}
function resolve(newValue) {
state = "fulfilled"; // 异步操作完成后将state置为fulfilled
value = newValue;
setTimeout(() => {
deferreds.forEach((deferred) => {
deferred(value);
});
}, 0);
}
fn(resolve);
}
异步操作成功之后,state会变成fulfilled,这之后then注册的回调函数都会立即执行。
以上基本实现了Promise的所有基础功能,但真正魔幻般的部分尚未开始,详见下篇~
参考资料:剖析 Promise 之基础篇