Promise 的实现原理
Promise 的好处就不多说了,本文采用TDD(测试驱动开发)的形式去实现一个Promise。
测试库用jest,本文不会去介绍jest的用法。
第1个测试用例
test('测试Promise是构造函数, 且原型上有then方法', () => {
const promise = new Promise(() => {})
expect(promise.constructor).toEqual(Promise)
expect(promise).toEqual(expect.objectContaining({ then: expect.any(Function)}))
})
要实现的功能:
- Promise 实例是通过构造函数的形式实现,接收的参数是一个函数
- 其原型上有一个 then 方法,then方法接收二个参数,这两个参数都是函数
这个还是比较简单的直接上代码
实现
function Promise(fn) {}
Promise.prototype.then = function(onFulfilled, onRejected) {}
第2个测试用例
test('测试Promise构造函数的参数接收一个函数,并且会立即执行', () => {
let called = false;
new Promise(function () {
called = true;
})
expect(called).toBeTruthy()
})
test('测试resove和reject是函数', () => {
const mockFunction = jest.fn(function(resolve,reject) {});
const promsie = new Promise(mockFunction)
expect(typeof mockFunction.mock.calls[0][0] === 'function').toBeTruthy()
expect(typeof mockFunction.mock.calls[0][1] === 'function').toBeTruthy()
})
要实现的功能:
- Promise 构造函数的参数接收两个参数,第一个是resolve,第二个是reject,这两个都是函数
- 在 new Promise() 时构造函数的参数是立即执行的
- 调用 resolve() 时还可以传入一个值
第2个测试用例实现的难度一下子上去了,不能直接上代码了,我们要分析一下
分析:
我们知道 Promise 实例有三个状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败), 创建时的初始状态是pending,执行resolve()后会变成 fulfilled,并且调用resolve()时还要传一个值,这个值还要找个地方保存起来。
有了上面的分析我们知道Promise要有一个能保存状态的变量,还要又一个能保存resolve值的变量
实现
function Promise(fn) {
// 保存状态的变量
// 0 - pending
// 1 - fulfilled
// 2 - rejected
this._state = 0;
// 保存值的变量
this._value = null;
// 封装一个函数调用fn
doResolve(fn, this)
}
function doResolve(fn, promise) {
var done = false;
// 立即调用fn,并且传入 resolve 和 reject
var res = fn(function (value) {
if (done) return;
done = true;
resolve(promise, value)
}, function(reason) {
})
}
function resolve(self, newValue) {
// 更改状态为fulfilled
self._state = 1
// 保存值
self._value = newValue
}
第3个测试用例
test('测试promise resolve 了调用then方法', (done) => {
new Promise(function (resolve) {
process.nextTick(function () {
resolve({ value: 1 })
});
}).then(result => {
expect(result).toEqual({ value: 1 })
done()
})
})
要实现的功能:
- 调用 resolve(1) 后,p1.then的回调函数要能够执行,并且能够接收到resolve传过来的值(本例中就是1)
分析:
resolve() 执行后要能够执行then的回调函数,首先想到的是要把then的回调函数保存到一个地方,等到promise实例的状态变为fulfilled时,就去执行该回调函数,为了能够保存then的回调函数,我们需要定义一个新的类Handler。
实现
function Promise(fn) {
// 保存状态的变量
// 0 - pending
// 1 - fulfilled
// 2 - rejected
// 3 - adopted the state of another promise
this._state = 0;
this._deferredState = 0;
// 保存值的变量
this._value = null;
this._deferreds = null
// 封装一个函数调用fn
doResolve(fn, this)
}
Promise.prototype.then = function(onFulfilled, onRejected) {
// new Handler时把then的回调函数保存到Handler实例
// 还需要一个handle函数来关联Promise实例和Handler实例
handle(this, new Handler(onFulfilled, onRejected))
}
function Handler(onFulfilled, onRejected) {
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
this.onRejected = typeof onRejected === 'function' ? onRejected : null;
}
function handle(self, deferred) {
if (self._state === 0) {
if (self._deferredState === 0) {
self._deferredState = 1
self._deferreds = deferred; // 把handler实例挂到promise对象的_deferreds属性上
return;
}
}
handleResolved(self, deferred)
}
// 真正调用then中回调的地方
function handleResolved(self, deferred) {
setTimeout(function() {
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected
if (cb === null) {
return
}
var ret = cb(self._value)
}, 0)
}
function resolve(self, newValue) {
// 更改状态为fulfilled
self._state = 1
// 保存值
self._value = newValue
// 处理resolve后的逻辑
finale(self)
}
function finale(self) {
if (self._deferredState === 1) {
handle(self, self._deferreds) // handle中会进入handleResolved方法
self._deferreds = null
}
}
function doResolve(fn, promise) {
var done = false;
// 立即调用fn,并且传入 resolve 和 reject
var res = fn(function (value) {
if (done) return;
done = true;
resolve(promise, value)
}, function(reason) {
})
}
此时已经实现了一个最简单Promise,还有一个最重要的功能没有实现就是链式调用,继续完善
第4个测试用例
test('测试promise.then返回一个新的promise实例', (done) => {
const promise1 = new Promise(function (resolve) {
process.nextTick(function () {
resolve({ value: 1 })
});
})
const promise2 = promise1.then(result => {
done()
})
expect(promise2 instanceof Promise).toBeTruthy()
})
test('测试下一个then能够接收到上一个then的返回值', (done) => {
const promise1 = new Promise(function (resolve) {
process.nextTick(function () {
resolve({ value: 1 })
});
})
const promise2 = promise1.then(result => {
return { value: 2 }
})
promise2.then(result => {
expect(result).toEqual({ value: 2 })
done()
})
})
分析: 如果p1.then()的返回值仍然是一个Promise实例,我们就可以实现链式调用了,并且p2.then的回调能够接收上一个then返回的值
要实现功能:
- Promise.prototype.then 返回一个新的Promise实例
- 下一个then能够接收到上一个then的返回值
实现
function noop() {}
Promise.prototype.then = function(onFulfilled, onRejected) {
var res = new Promise(noop)
// new Handler时把then的回调函数保存到Handler实例
// 把then中返回的promise也保存到Handler实例
// 还需要一个handle函数来关联Promise实例和Handler实例
handle(this, new Handler(onFulfilled, onRejected, res))
return res
}
function Handler(onFulfilled, onRejected, promise) {
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
this.onRejected = typeof onRejected === 'function' ? onRejected : null;
this.promise = promise;
}
// 真正调用then中回调的地方
function handleResolved(self, deferred) {
setTimeout(function() {
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected
if (cb === null) {
return
}
var ret = cb(self._value)
// 用上一个then返回的promise,和then回调函数的返回值作为参数再调用resolve
resolve(deferred.promise, ret)
}, 0)
}
上面就实现了then的链式调用
第5个测试用例
test('测试嵌套promise', (done) => {
let resolveA, resolveB, resolveC
const A = new Promise(function (resolve, reject) {
resolveA = resolve;
});
const B = new Promise(function (resolve, reject) {
resolveB = resolve;
});
const C = new Promise(function (resolve, reject) {
resolveC = resolve;
});
resolveA(B);
resolveB(C);
resolveC('foo');
A.then(result => {
expect(result).toBe('foo')
done()
})
})
要实现的功能
- resolve的值是一个promise对象时,此promise的决议结果要依赖上一个promise的决议
实现
var LAST_ERROR = null;
var IS_ERROR = {};
function getThen(obj) {
try {
return obj.then;
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
function resolve(self, newValue) {
// 判断newValue是promise对象
if (newValue && typeof newValue === 'object') {
var then = getThen(newValue)
if (then === self.then && newValue instanceof Promise) {
self._state = 3
self._value = newValue
finale(self)
return
}
}
// 更改状态为fulfilled
self._state = 1
// 保存值
self._value = newValue
// 处理resolve后的逻辑
finale(self)
}
function handle(self, deferred) {
// 如果resolve() 的是一个prmise对象
while(self._state === 3) {
self = self._value
}
if (self._state === 0) {
if (self._deferredState === 0) {
self._deferredState = 1
self._deferreds = deferred; // 把handler实例挂到promise对象的_deferreds属性上
return;
}
}
handleResolved(self, deferred)
}
上面已经实现了一个可以链式调用,且支持 resolve Promise对象的功能,下面我们把reject的功能加上,整理出来一份完整代码
function noop() {}
var LAST_ERROR = null;
var IS_ERROR = {};
function getThen(obj) {
try {
return obj.then;
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
function Promise(fn) {
this._deferredState = 0;
this._state = 0;
this._value = null;
this._deferreds = null;
if (fn === noop) return
doResolve(fn, this)
}
Promise.prototype.then = function(onFulfilled, onRejected) {
var res = new Promise(noop)
handle(this, new Handler(onFulfilled, onRejected, res))
return res
}
function Handler(onFulfilled, onRejected, promise) {
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
this.onRejected = typeof onRejected === 'function' ? onRejected : null;
this.promise = promise;
}
function resolve(self, newValue) {
if (newValue === self) {
return reject(
self,
new TypeError('A promise cannot be resolved with itself.')
)
}
if (
newValue &&
(typeof newValue === 'object' || typeof newValue === 'function')
) {
var then = getThen(newValue)
if (then === IS_ERROR) {
return reject(self, LAST_ERROR)
}
if (then === self.then && newValue instanceof Promise) { // 处理resolve(promise)
self._state = 3
self._value = newValue
finale(self)
return
} else if (typeof then === 'function') { // 处理传给resolve的是thenable 对象({ then: function })
doResolve(then.bind(newValue), self)
}
}
self._state = 1
self._value = newValue
finale(self)
}
function reject(self, newValue) {
self._state = 2;
self._value = newValue;
if (Promise._onReject) {
Promise._onReject(self, newValue);
}
finale(self);
}
function finale(self) {
if (self._deferredState === 1) {
handle(self, self._deferreds);
self._deferreds = null
}
if (self._deferredState === 2) {
for (var i = 0; i < self._deferreds.length; i++) {
handle(self, self._deferreds[i])
}
self._deferreds = null
}
}
function handle(self, deferred) {
while(self._state === 3) {
self = self._value
}
if (self._state === 0) {
if (self._deferredState === 0) {
self._deferredState = 1;
self._deferreds = deferred;
return;
}
/***
* 此情况是针对如下场景
* const p = new Promise()
* p.then()
* p.then()
*
***/
if (self._deferredState === 1) {
self._deferredState = 2;
self._deferreds = [self._deferreds, deferred];
return;
}
self._deferreds.push(deferred);
return;
}
handleResolved(self, deferred)
}
function handleResolved(self, deferred) {
setTimeout(function() {
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
if (cb === null) {
if (self._state === 1) {
resolve(deferred.promise, self._value);
} else {
reject(deferred.promise, self._value);
}
return
}
var ret = cb(self._value)
resolve(deferred.promise, ret)
}, 0)
}
function doResolve(fn, promise) {
var done = false;
var res = fn(function (value) {
if (done) return;
done = true;
resolve(promise, value)
}, function(reason) {
if (done) return;
done = true;
reject(promise, reason);
})
}
Promise的静态方法 all/resolve/reject/reae 的实现
function valuePromise(value) {
var p = new Promise(noop)
p._state = 1
p._value = value
return p
}
Promise.resolve = function (value) {
if (value instanceof Promise) return value
return valuePromise(value)
}
Promise.reject = function(value) {
return new Promise(function(resolve, reject) {
reject(value)
})
}
Promise.all = function(arr) {
return new Promise(function (resolve, reject) {
if (arr.length === 0) return resolve([])
var remaining = arr.length
function res(i, val) {
if (val && (typeof val === 'object' || typeof val === 'function')) {
if (val instanceof Promise && val.then === Promise.prototype.then) {
if (val._state === 1) return res(i, val._value)
if (val._state === 2) reject(val._value)
// 循环调用then方法直到决议
val.then(function(val) {
res(i, val)
}, reject)
return
}
}
// 成功决议
args[i] = val
// 所有promise全部成功决议
if (--remaining === 0) {
resolve(args)
}
}
for(var i = 0; i < arr.length; i++) {
res(i, arr[i])
}
})
}
Promise.race = function (values) {
return new Promise(function (resolve, reject) {
values.forEach(function(value) {
Promise.resolve(value).then(resolve, reject)
})
})
}
测试用例的执行结果:
至此我们实现了一个Promise