手写Promise笔记

手写Promise笔记

掘金上看见一个20行的Promise实现,主要是链式调用,感觉很不错,学习学习。

原文出处:最简实现Promise,支持异步链式调用(20行)

Promise

直接上代码吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Promise构造函数
function Promise(excutor) {
var self = this;
self.onResolvedCallback = [];
function resolve(value) {
setTimeout(() => {
self.data = value;
self.onResolvedCallback.forEach(callback => callback(value));
},0);
}
excutor(resolve.bind(self));
}
// Promise的then调用函数
Promise.prototype.then = function(onResolved) {
var self = this;
return new Promise(resolve => {
self.onResolvedCallback.push(function() {
var result = onResolved(self.data);
if (result instanceof Promise) {
result.then(resolve);
} else {
resolve(result);
}
});
})
}

参数excutor为一个函数,这个函数有两个参数。

一个是resolve,一个是reject,这个实现为简单实现,所以没有第二个参数,主要为实现链式调用。

Promise构造函数中的onResolvedCallback为一个待调用的函数数组。

当通过then函数注册回调的时候,会把回调函数存在onResolvedCallback数组中。

then函数规范中有提到,必须返回一个新的Promise对象,基于这个可以实现链式调用。

then如何返回也在规范中有提及,如果回调函数返回了一个Promise,那么then返回的Promise就要使用函数返回的Promise的值(value)。

如果不是函数返回的不是Promise,那么then返回的Promise就直接以这个值来解决。

resolve函数是一个完成函数,会把Promise的状态从pending转为resolved,并且调用全部注册的回调。

resolve为啥要通过setTimeout调用呢?可以看下面的代码。

1
2
3
4
5
6
7
8
var promise = new Promise(function(resolve,reject) {
resolve('success');
});
// 上面的操作就已经调用了数组中的回调
// 下面的注册的回调就会无效了
promise.then(res=>{
console.log(res);
});

我们构建了一个promise,直接的将这个promise解决掉(调用resolve)。

resolve会把onResolvedCallback的函数逐一地执行。

注意,这时候的操作都是同步的,也就是这时的then注册回调还没有执行,导致了then方法没有被执行。

使用了setTimeout,使得更改状态以一个新的任务执行,也就是在当前代码执行完之后再执行,使得回调函数可以注册到onResolvedCallback里面。使得then的表现正常。

需要注意,Promise应该是以微任务的形式来进行resolve的,而不是以任务,这个在之前的关于taskmicrotask的文章中有说到。