前几天翻译了promise/A+的规范,感觉自己又行了,所以试一下手撸一下promise
,目标两点:
- 完全符合promise/A+规范
- 实现的promise能够通过官方的测试case
开始之前多嘱咐一句,不管是阅读本文,还是想自己手撸promise
,首先一定要熟悉promise/A+
规范的内容,不了解的同学,可以查看这里 Promises/A+ 规范(译文)
1. 初步实现
1.1 promise的属性
作为使用过promise的同学(当然了,如果没使用过,可以先去看看 es6 Promise 对象),应该知道promise包含有以下属性和方法:
- 属性 :
promiseState
:promise
的状态promiseResult
:当前promise
的结果,比如resolve
的value
和reject
的reason
microTaskQuene
: 存储当前promise
相关的微任务队列,看网上一些资料,有的实现会设置两个数组,分别来存储resolve
和reject
,本质是一个意思(可能没有动手实现过的同学,到这就有点上强度了,但是不要怕,后续会有它的更详细的介绍)
- 方法 :
resolve
: 要和new Promise((resolve, reject) => {})
中的resolve
做一个区分,new Promise((resolve, reject) => {})
中的resolve
只是一个形参,换成任何一个合法的函数名字均可,这里的resolve
单指Promise
上的方法, 主要作用是返回一个状态被设置为fulfilled
的promise
对象,后面再展开reject
: 注意事项同上,作用是返回一个状态被设置为rejected
的promise
对象then
:【原型方法】promise
状态被设置为fulfilled
的回调函数catch
: 【原型方法】promise
状态被设置为rejected
的回调函数finally
: 【原型方法】promise
不管状态被设置为谁,最终一定会执行的回调函数all
: 将多个promise
包装成一个promise
,只有都执行成功 或者任何一个失败才会调用race
: 将多个promise
包装成一个promise
,任何一个异步改变状态(注意,这里是不论成功失败)就会调用allSettled
: 将多个promise
包装成一个promise
,只有所有的异步均执行结束才会调用,主要和all
的区别是,当有异步失败的情况下,allSettled
也必须等到所有都执行完成才会执行any
: 这里是和race
对比,当有任何一个异步成功,即为成功,区别是,必须所有的异步都失败 ,any
才会置成失败try
: 提案阶段,来解决promise无法捕获异步异常的问题
看到这,不要怕,上面只是对于promise对象的整理和简述,让大家有一个初步的认识,感觉有点模糊还是要翻翻阮一峰的文档(我写上面还是又翻了一遍,确实细节多,一次一个收获):
这里的图中也是对应上面加粗的是promise核心的方法和属性,也是我们本次手撸的重点对象,其他的几个方法,后续看情况要不要实现。
1.2 代码结构
到这里应该大致知道了什么是promise,算是promise大致的框架,到这里,我们就可以写出promise的大致的框架:
javascript
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
#promiseState = PENDING;
#promiseResult = undefined;
constructor(executor) {
// 注意和下面的#resolve和#reject方法区别
let resolve = (value) => {}
let reject = (reason) => {}
executor(resolve, reject)
}
then(){}
catch(){}
finally(){}
// 下面是在Promise类上面的方法
static resolve(value){}
static reject(reason){}
static race([]){}
static all([]){}
static allSettled([]){}
static try(f){}
}
我们这里使用了class
来定义promise
,promise/A+
规范来说,只要是实现了then
方法的对象或者函数均能称为promise
,所以同学应该也能从网上function
的版本。使用class
的话,使用静态方法和私有属性,看起来更优雅。
上面有几点需要注意:
- promise的状态有3种:
pending
,fulfilled
,rejected
,所以我们定义三个常量 - 如果我们打印promise 对象,可以看到其实有两个属性
promiseState
,promiseResult
,其中promiseState
的初始状态为pending,promiseResult
初始值为undefined
,同时因为我们不希望除了内部的resolve和reject方法修改这两个值,所以我们将这两个属性设置为class的私有属性 - 我们
let p = new Promise((resolve, reject) => {})
之后打印p
可以看到只有then
、catch
、finally
方法,其他的几个方法只能通过类Promise
进行调用,所以正好符合class的静态方法
ok,到这里基本有了promise的基本结构啦,下面开始具体的实现:
1.3 constructor方法
我们就自上而下开始写吧,首先看一下这个class的构造方法怎么实现,这里我们首先要想到我们是怎么使用promise:
javascript
let p = new Promise((resolve, reject) => {
// 干点啥
})
所以可以知道promise的构造函数的参数是一个方法,方法的参数是两个方法resolve和reject,而这两个方法的主要作用是更新我们promise的状态,所以具体实现如下:
javascript
constructor(executor) {
let myResolve = (value) => {
if (this.#promiseState === PENDING) {
this.#promiseState = FULFILLED;
this.#promiseResult = value;
// todo 这里有伏笔
}
}
let myReject = (reason) => {
if (this.#promiseState === PENDING) {
this.#promiseState = REJECTED;
this.#promiseResult = reason;
// todo 这里有伏笔
}
}
try {
executor(myResolve, myReject);
} catch (e) {
this.#promiseState = REJECTED;
this.#promiseResult = e;
}
}
上面的代码有几点需要注意:
- 上面其实也提到了,我们这里executor的参数名字 只是一个形参 ,叫啥都可以,大家千万不要和
Promise.resolve
和Promise.reject
混淆了,我这里也是特意换了一个名字做了区分(不瞒大家,我最开始就以为他俩是一个东西,谁知道真不是个东西!) promise
的特性是
2.1 状态一旦变更,就不会再发生改变
2.2 且只有pending
状态下的promise
可以被改变状态,所以我们做了状态的判断和限制- 在执行
executor
时可能会抛出异常,一旦抛出来异常,promise
的状态也要设置为reject
(这一点也是容易疏漏的)
1.3.1 遗留的问题
- 这里的 myReject和myReject是不是写完整了,很显然没有,我在对应的位置留下了todo,在后面会解答,大家可以先思考一下
- resolve的方法,当我们进行如下调用:
javascript
let p = new MyPromise((resolve, reject) => {
resolve({
then: (resolve2, reject2) => {
reject2(2);
}
})
})
console.log(p);
按照我们上面的写法,最终会返回一个promise,状态是是fulfilled,value是我们传入的对象:
javascript
MyPromise {
status: 'fulfilled',
value: { then: [Function: then] }
}
但是实际上promise的表现为:
javascript
let p = new Promise((resolve, reject) => {
resolve({
then: (resolve2, reject2) => {
reject2(2);
}
})
})
console.log(p);
//这里打印成pending的原因涉及到promise的执行
// 可以理解为then方法在下次循环才会执行
// Promise { <pending> }
// 所以
p.then(res => {
console.log('res', res);
}).catch(e => {
console.log('catch', e);
});
// catch 2
这里大家姑且先这么看着,在下面讲到resolve方法的时候,再详细展开
1.4 then 方法
上面也提到了,只要是实现了then方法的对象或者函数,均可称为promise,可见then在promise中的位置之重。而promise/A+规范中基本就是在指导我们如何实现一个then方法,我们接下来也是按照promise/A+
规范一步步的来实现then
方法:
注意:这里因为规范有些细节写的不是很明确,所以我们同时会参考规范对应的测试case进行逆向分析
首先then方法的语法如下:
javascript
promise.then(onFulfilled, onRejected)
1.4.1 规范「2.2.1」
onFulfilled
和onRejected
都是可选的参数:如果
onFulfilled
的类型不是函数,那它将一定被忽略如果
onRejected
的类型不是函数,那它将一定被忽略
所以这个规则比较好理解,但是不好实现,因为单从规范上我们看不出什么是被忽略,我最开始理解的是这样的:
javascript
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
return;
}
if (typeof onRejected !== 'function') {
return;
}
}
但是显然不是这样的,如果这么干,下面就会报错了
scss
p = Promise.resolve().then(undefine, undefine);
// 显然这里的p是undefine
p.then(() => {}, () =>)
所以我们要看一下针对这个规则,测试case是怎么写的,来看一下:
scss
describe("applied to a directly-rejected promise", function () {
function testNonFunction(nonFunction, stringRepresentation) {
specify("`onFulfilled` is " + stringRepresentation, function (done) {
//
rejected(dummy).then(nonFunction, function () {
done();
});
});
}
// 当 onFulfilled 是非函数的情况,不会影响onRejected的执行
testNonFunction(undefined, "`undefined`");
testNonFunction(null, "`null`");
testNonFunction(false, "`false`");
testNonFunction(5, "`5`");
testNonFunction({}, "an object");
});
describe("applied to a promise rejected and then chained off of", function () {
function testNonFunction(nonFunction, stringRepresentation) {
specify("`onFulfilled` is " + stringRepresentation, function (done) {
rejected(dummy).then(function () { }, undefined).then(nonFunction, function () {
done();
});
});
}
// 当onRejected 是非函数的情况,不会影响then之后的链式调用
testNonFunction(undefined, "`undefined`");
testNonFunction(null, "`null`");
testNonFunction(false, "`false`");
testNonFunction(5, "`5`");
testNonFunction({}, "an object");
});
describe("applied to a directly-fulfilled promise", function () {
function testNonFunction(nonFunction, stringRepresentation) {
specify("`onRejected` is " + stringRepresentation, function (done) {
resolved(dummy).then(function () {
done();
}, nonFunction);
});
}
// 当 onRejected 是非函数的情况,不会影响 onFulfilled 的执行
testNonFunction(undefined, "`undefined`");
testNonFunction(null, "`null`");
testNonFunction(false, "`false`");
testNonFunction(5, "`5`");
testNonFunction({}, "an object");
});
describe("applied to a promise fulfilled and then chained off of", function () {
function testNonFunction(nonFunction, stringRepresentation) {
specify("`onRejected` is " + stringRepresentation, function (done) {
resolved(dummy).then(undefined, function () { }).then(function () {
done();
}, nonFunction);
});
}
// 当 onFulfilled 是非函数的情况,不会影响then之后的链式调用
testNonFunction(undefined, "`undefined`");
testNonFunction(null, "`null`");
testNonFunction(false, "`false`");
testNonFunction(5, "`5`");
testNonFunction({}, "an object");
});
不要看着代码长就怕,我们简化一下,得到下面四种情况:
javascript
// onFulfilled 是非函数
// 不影响onRejected的执行
1. promise.rejected().then(undefined, function () { })
// 可以直接将当前promise的已完成状态传递给链式调用的下一个promise
2. promise.resolve().then(undefined, function () { }).then(function() {}, undefined)
// onRejected 是非函数
// 不影响当前promise onFulfilled 的执行
3. promise.resolve().then(function () { }, undefined)
// 可以直接将当前promise的拒绝状态传递给链式调用的下一个promise
4. promise.rejected().then(function () { }, undefined).then(undefined, function() {})
这里的2和4用到规则2.7的知识,我们还没讲到,这里可以简单了解一下,后续会展开,即 then
方法必须返回个全新的promise ,所以才能进行链式的调用,所以简而言之,「虽然忽略了当前的非函数方法,但是依然会把当前promise的状态正确的传递给下一个环节」
这么一看图片是不是清晰一点,但是这里也有一个问题,传递给后续 promise
的 value
和 reason
是当前 promise
的吗,还是 onFulfilled
和 onRejected
?,话不多说,上代码吧:
javascript
Promise.resolve(1).then(undefined, () => {}).then((value) => {
console.log(value)
}, undefined);
Promise.reject(2).then(() => {}, undefine).then(undefined, (value) => {
console.log(value)
});
// 1
// 2
看起来是真的忽略了非函数的情况,所以这个时候我们就可以这么来写了:
javascript
then(onFulfilled, onRejected) {
let newPromise = new myPromise((reslove, reject) => {
if (this.#promiseState === FULFILLED) {
if (typeof onFulfilled !== 'function') {
// 把当前的value传递新的promise
reslove(this.#promiseState)
} else {
// todo 1
}
} else if (this.#promiseState === REJECTED) {
if (typeof onRejected !== 'function') {
// 把当前的season传递新的promise
reject(this.#promiseState)
} else {
// todo 2
}
} else {
// todo 3
}
});
// 这是2.7.2 的规则,这里先简单实现
return newPromise;
}
到这里应该能满足2.2.1 规范了,成功的一大步!!!坚持住~
1.4.2 规范「2.2.2」
如果
onFulfilled
是一个函数:它一定会在当前
promise
状态切换为fulfilled
时被调用,同时promise
的value
作为它的第一个参数。它一定不能在当前
promise
的状态切换为fulfilled
之前被调用。它只能被调用一次
这里需要注意两点:
-
onFulfilled
要在promise被切换为fulfilled
被调用,这就要分两种情况
1.1 第一种情况:调用 then 的时候, promise 的状态正好是fulfilled
javascriptlet p = new MyPromise((resolve, reject) => { resolve('success'); }) p.then(res => { console.log(res); }) // success 这种情况就比较好满足了
1.2 第二种情况:调用 then 的时候, promise 的状态正好是
pending
javascriptlet p = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('success'); }, 10000); }) p.then(res => { console.log(res); })
这也对应我们上面的todo 3 待补充的逻辑
-
它只能被调用一次 我们这里没有处理多次调用的问题,需要考虑一下怎么处理
接下来针对上面的几种情况我们进行code:
1.4.2.1 promise 的状态是 fulfilled
只写重要的代码片段哈
javascript
then(onFulfilled, onRejected) {
let newMyPromise = new MyPromise((resolve,reject) => {
if (this.#promiseState === FULFILLED) {
if (typeof onFulfilled !== 'function') {
// 把当前的value传递新的promise
resolve(this.#promiseState);
} else {
// 将当前的promise value作为第一个参数调用 onFulfilled
onFulfilled(this.#promiseState);
// 这个规范 到这里还没提到 是否调用resolve,我们先不处理
// resolve
}
}
});
return newMyPromise;
}
1.4.2.1 promise 的状态是 pending
这个是比较难处理的,这个也正是promise
的精髓所在,我们思考的大致逻辑是这样的:
1.当调用then
的时候,如果promise
是fulfilled
,那就直接调用 then
的 onFulfilled
方法
2.当调用then的时候,如果promise
是pending
,我们就需要把 onFulfilled
缓存 起来,在将来的某个时候,promise
状态变成 onFulfilled
的时候调用 它
所以这个关键词就俩:缓存 、调用
还记得在1.1 promise
属性的介绍里我增加了一个microTaskQuene
的属性,养兵千日,用兵一时,该它出场啦(其实到这个规范这里,我们设置一个对象或者变量就可以缓存onFulfilled
方法,之所以使用数组来存储的原因是,后面还有2.2.6规范要用,也显得我这个设计有远见,哈哈),上代码:
javascript
then(onFulfilled, onRejected) {
let newMyPromise = new MyPromise((resolve,reject) => {
if (this.#promiseState === PENDING) {
this.#microTaskQueue.push({
onFulfilled: () => {
try{
onFulfilled(this.#promiseResult)
}catch(e){
reject(e);
}
},
onRejected: () => {
setTimeout(() => {
try{
onRejected(this.#promiseResult)
} catch (e){
reject(e);
}
}, 0)
}
})
}
});
return newMyPromise;
}
这样就可以同时将onFulfilled
和onRejected
缓存起来啦,那什么时候调用呢,上面说的很清楚,即未来某个时刻promise
状态变成 onFulfilled
的时候调用 它,所以要修改我们的constructor
代码如下:
kotlin
constructor(executor) {
let myResolve = (value) => {
if (this.#promiseState === PENDING) {
this.#promiseState = FULFILLED;
this.#promiseResult = value;
// 增加下面的逻辑
this.#microTaskQueue.forEach(item => {
item.onFulfilled(value);
});
}
}
let myReject = (reason) => {
if (this.#promiseState === PENDING) {
this.#promiseState = REJECTED;
this.#promiseResult = reason;
// 增加下面的逻辑
this.#microTaskQueue.forEach(item => {
item.onRejected(reason);
});
}
}
try {
executor(myResolve, myReject);
} catch (e) {
this.#promiseState = REJECTED;
this.#promiseResult = e;
}
}
正好补充了我们上面的遗留的todo,那现在只剩下一个了问题啦: 它只能被调用一次 由于resolve和reject只会执行一次,所以看起来已经满足了只能调用一次的特性
到这里应该基本完成了2.2.2的规范
小结
到这里的时候我们的代码如下:
csharp
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
#promiseState = PENDING;
#promiseResult = undefined;
#microTaskQueue = [];
constructor(executor) {
let myResolve = (value) => {
if (this.#promiseState === PENDING) {
this.#promiseState = FULFILLED;
this.#promiseResult = value;
// 增加下面的逻辑
this.#microTaskQueue.forEach(item => {
item.onFulfilled(value);
});
}
}
let myReject = (reason) => {
if (this.#promiseState === PENDING) {
this.#promiseState = REJECTED;
this.#promiseResult = reason;
// 增加下面的逻辑
this.#microTaskQueue.forEach(item => {
item.onRejected(reason);
});
}
}
try {
executor(myResolve, myReject);
} catch (e) {
this.#promiseState = REJECTED;
this.#promiseResult = e;
}
}
then(onFulfilled, onRejected){
let newMyPromise = new MyPromise((resolve,reject) => {
if (this.#promiseState === FULFILLED) {
if (typeof onFulfilled !== 'function') {
// 把当前的value传递新的promise
resolve(this.#promiseResult);
} else {
// 将当前的promise value作为第一个参数调用 onFulfilled
onFulfilled(this.#promiseResult);
// 这个规范 到这里还没提到 是否调用resolve,我们先不处理
// resolve
}
} if (this.#promiseState === REJECTED) {
if (typeof onFulfilled !== 'function') {
// 把当前的value传递新的promise
reject(this.#promiseResult);
} else {
// 将当前的promise value作为第一个参数调用 onFulfilled
onRejected(this.#promiseResult);
// 这个规范 到这里还没提到 是否调用resolve,我们先不处理
// resolve
}
} else if (this.#promiseState === PENDING) {
this.#microTaskQueue.push({
onFulfilled: () => {
try{
onFulfilled(this.#promiseResult)
}catch(e){
reject(e);
}
},
onRejected: () => {
setTimeout(() => {
try{
onRejected(this.#promiseResult)
} catch (e){
reject(e);
}
}, 0)
}
})
}
});
return newMyPromise;
}
catch(){}
finally(){}
// 下面是在Promise类上面的方法
static resolve(value){}
static reject(reason){}
static race([]){}
static all([]){}
static allSettled([]){}
static try(f){}
}
1.4.3 规范「2.2.3」
如果
onRejected
是一个函数
2.2.3.1 它一定会在当前promise
状态切换为rejected
时被调用,同时promise
的reason
作为它的第一个参数。
2.2.3.2 它一定不能在当前promise
的状态切换为rejected
之前被调用。
2.2.3.3 它只能被调用一次
这里的实现和1.4.2一样,在小结中已经体现,这里就不多赘述啦
1.4.4 规范「2.2.4」
各位是不是和我一样,开始的看着是不是有点懵逼,看规范的注释解释:
这里的"平台代码"是指引擎、环境和
promise
实现代码。在实践中,这一要求确保onFulfilled
和onRejected
在调用事件循环之后异步执行,并使用新的堆栈。这可以用"宏任务"机制(如setTimeout
或setImmediate
)实现,也可以用"微任务"机制,如MutationObserver
或process.nextTick
实现。
推测像是执行onFulfilled
和 onRejected
要使用异步方式进行调用,而我们上面是采用的同步方式进行的,当然了,只是推测,我们看一下这个规范的测试case咋写的,这里比较相似,只挑两个代表看一下:
javascript
describe("`then` returns before the promise becomes fulfilled or rejected", function () {
testFulfilled(dummy, function (promise, done) {
var thenHasReturned = false;
promise.then(function onFulfilled() {
// 这里期望即使先调用了then方法,但是thenHasReturned仍被下面的代码改成了true,
// 也说明onFulfilled方法实际是在 thenHasReturned = true; 后面执行的
assert.strictEqual(thenHasReturned, true);
done();
});
thenHasReturned = true;
});
testRejected(dummy, function (promise, done) {
var thenHasReturned = false;
// 这里期望即使先调用了then方法,但是thenHasReturned仍被下面的代码改成了true,
// 也说明onRejected方法实际是在 thenHasReturned = true; 后面执行的
promise.then(null, function onRejected() {
assert.strictEqual(thenHasReturned, true);
done();
});
thenHasReturned = true;
});
});
这里如果展开说的话,就涉及到js的事件循环机制 ,感兴趣的同学可以自己先搜搜看看,我之后会有单独的文章来讲解,这里呢,大家可以简单理解为,onFulfilled
和 onRejected
的执行要使用异步执行,实现的方式可以使用用"宏任务"机制(如setTimeout
或setImmediate
)实现,也可以用"微任务"机制,如MutationObserver
或process.nextTick
实现,这里我们可以使用setTimeout
来实现异步效果,代码如下:
scss
then(onFulfilled, onRejected){
let newMyPromise = new MyPromise((resolve,reject) => {
if (this.#promiseState === FULFILLED) {
setTimeout(() => {
if (typeof onFulfilled !== 'function') {
resolve(this.#promiseResult);
} else {
try{
onFulfilled(this.#promiseResult);
}catch(e){
reject(e);
}
}
}, 0)
} if (this.#promiseState === REJECTED) {
setTimeout(() => {
if (typeof onRejected !== 'function') {
reject(this.#promiseResult);
} else {
try{
onRejected(this.#promiseResult);
}catch(e){
reject(e);
}
}
}, 0)
} else if (this.#promiseState === PENDING) {
this.#microTaskQueue.push({
onFulfilled: () => {
setTimeout(() => {
if (typeof onFulfilled !== 'function') {
resolve(this.#promiseResult);
} else {
try{
onFulfilled(this.#promiseResult);
}catch(e){
reject(e);
}
}
}, 0)
},
onRejected: () => {
setTimeout(() => {
if (typeof onRejected !== 'function') {
reject(this.#promiseResult);
} else {
try{
onRejected(this.#promiseResult);
}catch(e){
reject(e);
}
}
}, 0)
}
})
}
});
return newMyPromise;
}
这样就满足了异步调用的效果啦,继续继续!
1.4.5 规范「2.2.5」
onFulfilled
和onRejected
必须作为函数进行调用(即没有this
值) 也就是说,在严格模式下,这在它们内部是undefined
;在非严格模式下,它将是全局对象。
效果如下:
javascript
Promise.resolve().then(function() {"use strict";console.log(this)})
// undefined
Promise.resolve().then(function() {console.log(this)})
// 浏览器下是 window
// node环境下是 global
这里主要说明的是一种场景,即onFulfilled
和 onRejected
是作为对象的方法进行调用时的情况,如下面的代码:
javascript
class testClass {
constructor() {
this.value = 1;
}
test() {
console.log(this.value);
}
}
const obj = new testClass();
MyPromise.resolve().then(obj.test)
// 这里会抛出异常 提示 TypeError: Cannot read properties of undefined (reading 'value')
这里从测试case上并没有针对这个场景的测试,唯一做的就是当用户传递的不正确的时候,抛出这个异常,所以我们能做也是在onFulfilled
和 onRejected
调用时做好try catch,上一小节我们的代码已经处理,哈哈,明智吧
1.4.5 规范「2.2.6」
then
可能会在同一个promise
上多次进行调用如果
promise
的状态是已完成,所有的相应的onFulfilled
回调都必须按照调用的顺序执行。如果
promise
的状态是拒绝状态,所有的相应的onRejected
回调都必须按照调用的顺序执行。
说人话就是,then
可以被调用多次,而最终的回调都会按照调用的顺序进行。
这里问一个愚蠢的问题,我第一看这个规范的时候,想当然的以为是这样的:
scss
Promise.resolve().then().then().then()...then()
不知道有没有和我一样的,实际上是这样的:
javascript
let p = Promise.resolve();
p.then(() => console.log(1));
p.then(() => console.log(2));
p.then(() => console.log(3));
p.then(() => console.log(4));
所以这里终于用到了上面的 #microTaskQuene
属性了,promise 如果是 fulfilled 和 pending 状态下的promise 直接按顺序调用,顺序执行即可,只是pending状态下,就需要按照顺序缓存起来:
scss
this.#microTaskQueue.push({
onFulfilled: () => {
try{
onFulfilled(this.#promiseResult)
}catch(e){
reject(e);
}
},
onRejected: () => {
setTimeout(() => {
try{
onRejected(this.#promiseResult)
} catch (e){
reject(e);
}
}, 0)
}
})
在再需要的时候,按顺序进行执行:
ini
this.#microTaskQueue.forEach(item => {
item.onFulfilled(value);
});
上面的1.4.2的小节已经写过啦,就不过多赘述了
1.4.5 规范「2.2.7」
then
必须返回一个promise
[3.3],如下:promise2 = promise1.then(onFulfilled, onRejected);
1. 如果
onFulfilled
或onRejected
返回值一个值x
,则运行Promise
解析过程 [[Resolve]](promise2, x)。2.如果
onFulfilled
或者onRejected
抛出异常e
,则promise2
必须以e
作为reason
将promise2
置为rejected
3.如果
onFulfilled
不是函数且promise1
成功执行,promise2
必须和promise1
一样的value
将状态置为fulfilled
4.如果
onRejected
不是函数且promise1
拒绝执行,promise2
必须拒绝执行并返回和promise1
相同的reason
还记得我们上面代码,怕你们忘了,我再贴一遍:
scss
setTimeout(() => {
try{
// 这里我们 只是 调用onFulfilled ,并没有处理返回promise的状态
onFulfilled(this.#promiseState);
}catch(e){
reject(e);
}
}, 0)
而对于promise
的链式调用来说,我们需要将当前promise
的状态传递给返回的promise
中,所以我们按照上面的4条规则,可以这样来写代码:
javascript
// 1. 如果 `onFulfilled` 或 `onRejected` 返回值一个值 `x`,则运行`Promise`解析过程 [[Resolve]](promise2, x)。
try {
let x = onFulfilled(this.#promiseResult)
// or
let x = onRejected(this.#promiseResult)
} catch(e) {
// 2.如果 `onFulfilled` 或者 `onRejected` 抛出异常 `e` ,则 `promise2` 必须以`e`作为`reason`将`promise2`置为`rejected`
reject(e);
}
// 调用[[Resolve]](promise2, x)
// 我们命名resolvePromise
resolvePromise(newPromise, x);
这里注意两点:
- 上面的3和4我们在处理2.2.1 规则的时候已经通过分析测试case解决了(此处应该有掌声)
resolvePromise
具体怎么实现,我们这里先不用关心,会单独抽取一个方法实现,而且透露一下,上面只传递两个参数是不够的,后面再讨论
到此为止,关于then
方法的实现到此结束,看看我们的成果吧;
javascript
then(onFulfilled, onRejected){
let newMyPromise = new MyPromise((resolve,reject) => {
if (this.#promiseState === FULFILLED) {
setTimeout(() => {
if (typeof onFulfilled !== 'function') {
resolve(this.#promiseResult);
} else {
try{
let x = onFulfilled(this.#promiseResult);
resolvePromise(newMyPromise, x);
}catch(e){
reject(e);
}
}
}, 0)
} if (this.#promiseState === REJECTED) {
setTimeout(() => {
if (typeof onRejected !== 'function') {
resolve(this.#promiseResult);
} else {
try{
let x = onRejected(this.#promiseResult);
resolvePromise(newMyPromise, x);
}catch(e){
reject(e);
}
}
}, 0)
} else if (this.#promiseState === PENDING) {
this.#microTaskQueue.push({
onFulfilled: () => {
setTimeout(() => {
if (typeof onFulfilled !== 'function') {
resolve(this.#promiseResult);
} else {
try{
let x = onFulfilled(this.#promiseResult);
resolvePromise(newMyPromise, x);
}catch(e){
reject(e);
}
}
}, 0)
},
onRejected: () => {
setTimeout(() => {
if (typeof onRejected !== 'function') {
resolve(this.#promiseResult);
} else {
try{
let x = onRejected(this.#promiseResult);
resolvePromise(newMyPromise, x);
}catch(e){
reject(e);
}
}
}, 0)
}
})
}
});
return newMyPromise;
}
哈哈,你可能也发现代码比较重复了,但是我们先不着急优化哈,最后有的是空间!
1.4.6 resolvePromise方法
在规范中有一半的篇幅是关于这个方法的实现,可见其分量,而且它其实也是在then方法中进行调用,所以我也就把它归到then方法中啦。
2.3.1 如果
promise
和x
是同一个对象的引用, 则直接将TypeError
作为reason
拒绝promise
.
2.3.2 如果x
是一个promise
,则取决于它的状态 [3.4]:
2.3.2.1 如果x的状态是pending
,promise
必须在x
被执行和被拒绝之前保持pending
状态。
2.3.2.2 如果x
的状态是fulfilled
, 则使用相同的value
执行promise
.
2.3.2.3 如果x
的状态是rejected
,则使用相同的reason
拒绝promise
.
2.3.3 另外,如果x是一个对象或者函数时:
2.3.3.1 将then
方法赋值为x.then
. [3.5]
2.3.3.2 如果读取属性x.then
导致抛出异常e
,则以e
作为reason
拒绝promise
。
2.3.3.3 如果then
是一个函数,则将x
作为它的this
进行调用(可以理解为:x.then().bind(x))
,同时它的第一个参数 是resolvePromise
, 第二个参数是rejectPromise
,关于这两个参数:
2.3.3.3.1 如果使用value
y
调用resolvePromise
,则运行[[Resolve]](promise, y)
.
2.3.3.3.2 如果使用reason
r
调用rejectPromise
,则使用r
作为reason
拒绝promise
。
2.3.3.3.3 如果resolvePromise
和rejectPromise
都被调用,或者使用同一参数调用了多次,则优先采用第一次调用并且忽略其他所有后续的调用.
2.3.3.3.4 如果调用then
方法出现了一个异常e
:
2.3.3.3.4.1 如果resolvePromise
和rejectPromise
已经被调用,则忽略这个异常.
2.3.3.3.4.2 否则 将上述的e
作为reason
拒绝promise
.
2.3.3.4 如果then
不是一个函数,则直接使用x
作为value
来实现promise
.
2.3.4 如果x
不是一个对象或者函数,则直接使用x
作为value
来实现promise
.
篇幅不短,但是不要怕哈,像上面一样我们一条一条进行分析:
首先是函数的参数,规则中写的是:
lua
[[Resolve]](promise,x)
其中promise
是then方法需要返回的 newMyPromise
,x
是onFulfilled
或 onRejected
的返回值,但是看着规则,我们需要在后续的执行中来更新 newMyPromise
的状态,显然两个参数是不够的,需要将resolve
和reject
传入方法中:
javascript
function resolvePromise(newMyPromise, x, resolve, reject) {
// 2.3.1 如果 `promise` 和 `x` 是同一个对象的引用, 则直接将`TypeError`作为`reason`拒绝`promise`.
if (newMyPromise === x) {
reject(new TypeError('Chaining cycle'));
}
// 2.3.2 如果 `x` 是thenable, 使用它处理`promise`.
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else if (['[object Object]', '[object Function]'].includes(Object.prototype.toString.call(x))) {
// 2.3.3 另外,如果x是一个对象或者函数时:
try {
// 2.3.3.1 将then方法赋值为x.then
let then = x.then;
if (typeof then === 'function') {
let state = true;
try {
state = false;
// 2.3.3.3 如果then是一个函数,则将x作为它的this进行调
then.call(x, (y) => {
// 2.3.3.3.1 如果使用value y调用resolvePromise,则运行[[Resolve]](promise, y).
if (state) return;
state = true;
resolvePromise(newMyPromise, y, resolve, reject);
}, (r) => {
if (state) return;
state = true;
// 2.3.3.3.2 如果使用reason r调用 rejectPromise ,则使用r作为reason拒绝promise。
reject(r);
});
} catch (e) {
if (!state) {
// 2.3.3.3.4.2 否则 将上述的e作为 reason 拒绝promise.。
reject(e);
}
}
} else {
// 2.3.3.4 如果 then 不是一个函数,则直接使用x作为value来实现promise.
resolve(x);
}
} catch(e) {
// 2.3.3.2 如果读取属性x.then导致抛出异常e,则以e作为reason拒绝promise。
reject(e);
}
} else {
// 2.3.4 如果x不是一个对象或者函数,则直接使用x作为value来实现promise
resolve(x);
}
}
终于,终于把promise核心搞完了,剩下的就是小菜啦
1.5 resolve 方法
下面的方法没有规范可以参考啦,我们只能根据用法来实现了
Promise.resolve()
方法的主要作用是将现有对象转换为promise
对象:
- 参数是一个
Promise
实例 不做任何修改、原封不动地返回这个实例。 - 参数是一个
thenable
对象 会将这个对象转为Promise
对象,然后就立即执行thenable
对象的then()
方法。 - 参数不是具有
then()
方法的对象,或根本就不是对象 :返回一个新的Promise
对象,状态为resolved
。 - 不带有任何参数 : 允许调用时不带参数,直接返回一个
resolved
状态的Promise
对象。
所以和上面一样,我们来实现如下:
javascript
static resolve(value) {
// 参数是一个 Promise 实例
if (value instanceof MyPromise) {
return value;
}
if (typeof value === 'object' && typeof value.then === 'function') {
// 参数是一个thenable对象
// 这里看着阮一峰的翻译感觉有点不是很明白
// 但是很像promise/A+规范中 then方法的一句话
// 关于如果x是thenable,这种情况下,可以把x看作是一个promise,此时试图使promise采用x的状态。否则,它将用x作为value来完成promise。
let promise = new MyPromise((resolve, reject) => {
value.then((y) => {
// 2.3.3.3.1 如果使用value y调用resolvePromise,则运行[[Resolve]](promise, y).
resolvePromise(promise, y, resolve, reject);
}, (r) => {
// 2.3.3.3.2 如果使用reason r调用 rejectPromise ,则使用r作为reason拒绝promise。
reject(r);
});
});
} else {
// 参数是一个普通值或者为空
return new MyPromise((resolve, reject) => {
resolve();
});
}
}
这里的第二条比较难以处理和理解,可以看一个例子:
typescript
let obj = {
then: (resolve, reject) => {
resolve(2)
}
}
Promise.resolve(obj).then(res => {
console.log('res', res);
}).catch(e => {
console.log('catch', e);
})
// res 2
此时看着没问题,但是如果我更新一下:
typescript
let obj = {
then: (resolve, reject) => {
reject(2)
}
}
Promise.resolve(obj).then(res => {
console.log('res', res);
}).catch(e => {
console.log('catch', e);
})
// catch 2
所以resolve
的返回值promise
不一定是fulfilled
状态,同时也引出一个问题,Promise.resolve和promise(resolve => resolve())
是否等价,看一个例子:
javascript
let p = new Promise((resolved, rejected) => {
rejected(1);
});
let p1 = new Promise(resolved => resolved(p));
let p2 = Promise.resolve(p);
console.log(p, p1, p2, p1 === p, p2 === p);
忽略报错,我们可以看到打印
Promise { 1 }
Promise { }
Promise { 1 }
false
true
所以Promise的resolve方法和promise(resolve => resolve())是不等价的,也所以看到有的文章实现如下:
javascript
static resolve(value) {
// 参数是一个 Promise 实例
if (value instanceof MyPromise) {
return value;
}
return new MyPromise((resolve, reject) => {
resolve();
});
}
这里正好提到了我们在constructor中遗留的关于resolve的问题,有没有可能resolve就是这么写的,而是我们构造方法写的有问题呢?之所以放在这里讲的原因是我们在then方法中已经有了关于resolvePromise方法的实现,其中有关于value类型对于处理逻辑的影响,所以我们参考改造一下我们的构造方法:
javascript
constructor(executor) {
let myResolve = (value) => {
if (this.#promiseState === PENDING) {
if (value === this) {
myReject(new TypeError('Chaining cycle'));
}
// 2.3.3如果x是一个promise,则取决于它的状态.
if (value instanceof MyPromise) {
value.then(myResolve, myReject);
} else if (['[object Object]', '[object Function]'].includes(Object.prototype.toString.call(value))) {
// 2.3.3 另外,如果x是一个对象或者函数时:
try {
// 2.3.3.1 将then方法赋值为x.then
let then = value.then;
if (typeof then === 'function') {
let state = true;
try {
state = false;
// 2.3.3.3 如果then是一个函数,则将x作为它的this进行调
then.call(value, (y) => {
// 2.3.3.3.1 如果使用value y调用resolvePromise,则运行[[Resolve]](promise, y).
if (state) return;
state = true;
resolvePromise(this, y, myResolve, myReject);
}, (r) => {
if (state) return;
state = true;
// 2.3.3.3.2 如果使用reason r调用 rejectPromise ,则使用r作为reason拒绝promise。
myReject(r);
});
} catch (e) {
if (!state) {
// 2.3.3.3.4.2 否则 将上述的e作为 reason 拒绝promise.。
myReject(e);
}
}
} else {
// 2.3.3.4 如果 then 不是一个函数,则直接使用x作为value来实现promise.
this.#promiseState = FULFILLED;
this.#promiseResult = value;
// 增加下面的逻辑
this.#microTaskQueue.forEach(item => {
item.onFulfilled(value);
});
}
} catch(e) {
// 2.3.3.2 如果读取属性x.then导致抛出异常e,则以e作为reason拒绝promise。
myReject(e);
}
} else {
// 2.3.4 如果x不是一个对象或者函数,则直接使用x作为value来实现promise
this.#promiseState = FULFILLED;
this.#promiseResult = value;
// 增加下面的逻辑
this.#microTaskQueue.forEach(item => {
item.onFulfilled(value);
});
}
}
}
let myReject = (reason) => {
if (this.#promiseState === PENDING) {
this.#promiseState = REJECTED;
this.#promiseResult = reason;
// 增加下面的逻辑
this.#microTaskQueue.forEach(item => {
item.onRejected(reason);
});
}
}
try {
executor(myResolve, myReject);
} catch (e) {
this.#promiseState = REJECTED;
this.#promiseResult = e;
}
}
这么处理的话,看起来都OK了
1.6 reject 方法
reject
的实现和resolve
不同,我没写之前,我以为是和resolve
一样的,哈哈,没想到,你传递啥都是直接作为reason
给你抛出来,即使是上面的例子:
javascript
let obj = {
then: (resolve, reject) => {
resolve(2)
}
}
Promise.resolve(obj).then(res => {
console.log('res', res);
}).catch(e => {
console.log('catch', e);
})
// catch { then: [Function: then] }
// promise 也不例外,果然拒绝比接受容易多了
Promise.reject(new Promise((resolve, reject) => {})).then(res => {
console.log('res', res);
}).catch(e => {
console.log('catch', e);
})
// catch Promise { <pending> }
直接把obj最为reason扔出来了,所以代码如下:
javascript
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
1.7 catch 方法
Promise.prototype.catch()
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。
这一句话就够了:
kotlin
catch(func) {
return this.then(null, func);
}
1.8 finally 方法
finally()
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
这里我又犯了一个错误,我以为finally会在最后执行,不论then方法里面会执行多久,但是我又错了,看下面代码
javascript
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 1000)
}).then((res) => {
console.log(2.2)
setTimeout(() => {
console.log(2)
}, 1000)
}).then((res) => {
console.log(3.3)
setTimeout(() => {
console.log(3)
}, 1000)
}).then((res) => {
console.log(4.4)
setTimeout(() => {
console.log(4)
}, 1000)
}).finally(e => {
console.log(e)
})
// 打印
// 2.2
// 3.3
// 4.4
// undefined
// 2
// 3
// 4
// 调整一下finally的顺序
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 1000)
}).then((res) => {
console.log(2.2)
setTimeout(() => {
console.log(2)
}, 1000)
}).finally(e => {
console.log(e)
}).then((res) => {
console.log(3.3)
setTimeout(() => {
console.log(3)
}, 1000)
}).then((res) => {
console.log(4.4)
setTimeout(() => {
console.log(4)
}, 1000)
})
// 打印
// 2.2
// undefined
// 3.3
// 4.4
// 2
// 3
// 4
所以可以得到结论如下:
finally
是在promise
状态更新之后执行(并不是在所有then
和catch
之后执行),单纯只是表示状态变化一定会执行- 执行的时机和声明的顺序有关,按照声明的顺序进行执行
- 不会影响
promise
往后传递的值
所以代码如下:
javascript
finally(callback) {
return new MyPromise((resolve, reject) => {
if (this.#promiseState !== PENDING) {
setTimeout(() => {
callback();
if (this.#promiseState === FULFILLED) {
resolve(this.#promiseState);
} else {
reject(this.#promiseState);
}
}, 0)
} else {
this.#microTaskQueue.push({
onFulfilled: () => {
setTimeout(() => {
callback();
resolve(this.#promiseState);
}, 0)
},
onRejected: () => {
setTimeout(() => {
callback();
reject(this.#promiseState);
}, 0)
}
})
}
})
}
写到这是不是回过来一点味,这个逻辑不就是要,promise只要状态改变的时候,执行finally的回调,同时返回一个promise,借一下阮一峰的文档中的描述:
所以可以优化一下代码:
javascript
function resolvePromise(newMyPromise, x, resolve, reject) {
// 2.3.1 如果 `promise` 和 `x` 是同一个对象的引用, 则直接将`TypeError`作为`reason`拒绝`promise`.
if (newMyPromise === x) {
reject(new TypeError('Chaining cycle'));
}
// 2.3.3如果x是一个promise,则取决于它的状态.
if (x instanceof MyPromise) {
x.then(y => resolvePromise(newMyPromise, y, resolve, reject), reject);
} else if (['[object Object]', '[object Function]'].includes(Object.prototype.toString.call(x))) {
// 2.3.3 另外,如果x是一个对象或者函数时:
try {
// 2.3.3.1 将then方法赋值为x.then
let then = x.then;
if (typeof then === 'function') {
let state = true;
try {
state = false;
// 2.3.3.3 如果then是一个函数,则将x作为它的this进行调
then.call(x, (y) => {
// 2.3.3.3.1 如果使用value y调用resolvePromise,则运行[[Resolve]](promise, y).
if (state) return;
state = true;
resolvePromise(newMyPromise, y, resolve, reject);
}, (r) => {
if (state) return;
state = true;
// 2.3.3.3.2 如果使用reason r调用 rejectPromise ,则使用r作为reason拒绝promise。
reject(r);
});
} catch (e) {
if (!state) {
// 2.3.3.3.4.2 否则 将上述的e作为 reason 拒绝promise.。
reject(e);
}
}
} else {
// 2.3.3.4 如果 then 不是一个函数,则直接使用x作为value来实现promise.
resolve(x);
}
} catch(e) {
// 2.3.3.2 如果读取属性x.then导致抛出异常e,则以e作为reason拒绝promise。
reject(e);
}
} else {
// 2.3.4 如果x不是一个对象或者函数,则直接使用x作为value来实现promise
resolve(x);
}
}
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
#promiseState = PENDING;
#promiseResult = '';
#microTaskQueue = [];
constructor(executor) {
let myResolve = (value) => {
if (this.#promiseState === PENDING) {
if (value === this) {
myReject(new TypeError('Chaining cycle'));
}
// 2.3.3如果x是一个promise,则取决于它的状态.
if (value instanceof MyPromise) {
value.then(myResolve, myReject);
} else if (['[object Object]', '[object Function]'].includes(Object.prototype.toString.call(value))) {
// 2.3.3 另外,如果x是一个对象或者函数时:
try {
// 2.3.3.1 将then方法赋值为x.then
let then = value.then;
if (typeof then === 'function') {
let state = true;
try {
state = false;
// 2.3.3.3 如果then是一个函数,则将x作为它的this进行调
then.call(value, (y) => {
// 2.3.3.3.1 如果使用value y调用resolvePromise,则运行[[Resolve]](promise, y).
if (state) return;
state = true;
resolvePromise(this, y, myResolve, myReject);
}, (r) => {
if (state) return;
state = true;
// 2.3.3.3.2 如果使用reason r调用 rejectPromise ,则使用r作为reason拒绝promise。
myReject(r);
});
} catch (e) {
if (!state) {
// 2.3.3.3.4.2 否则 将上述的e作为 reason 拒绝promise.。
myReject(e);
}
}
} else {
// 2.3.3.4 如果 then 不是一个函数,则直接使用x作为value来实现promise.
this.#promiseState = FULFILLED;
this.#promiseResult = value;
// 增加下面的逻辑
this.#microTaskQueue.forEach(item => {
item.onFulfilled(value);
});
}
} catch(e) {
// 2.3.3.2 如果读取属性x.then导致抛出异常e,则以e作为reason拒绝promise。
myReject(e);
}
} else {
// 2.3.4 如果x不是一个对象或者函数,则直接使用x作为value来实现promise
this.#promiseState = FULFILLED;
this.#promiseResult = value;
// 增加下面的逻辑
this.#microTaskQueue.forEach(item => {
item.onFulfilled(value);
});
}
}
}
let myReject = (reason) => {
if (this.#promiseState === PENDING) {
this.#promiseState = REJECTED;
this.#promiseResult = reason;
// 增加下面的逻辑
this.#microTaskQueue.forEach(item => {
item.onRejected(reason);
});
}
}
try {
executor(myResolve, myReject);
} catch (e) {
this.#promiseState = REJECTED;
this.#promiseResult = e;
}
}
static resolve(value) {
// 参数是一个 Promise 实例
if (value instanceof MyPromise) {
return value;
}
// return new MyPromise((resolve, reject) => {
// resolve(value);
// });
return new MyPromise((resolve, reject) => {
resolve(value);
});
}
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
then(onFulfilled, onRejected){
let newMyPromise = new MyPromise((resolve,reject) => {
if (this.#promiseState === FULFILLED) {
setTimeout(() => {
if (typeof onFulfilled !== 'function') {
resolve(this.#promiseResult);
} else {
try{
let x = onFulfilled(this.#promiseResult);
resolvePromise(newMyPromise, x, resolve, reject);
}catch(e){
reject(e);
}
}
}, 0)
} if (this.#promiseState === REJECTED) {
setTimeout(() => {
if (typeof onRejected !== 'function') {
reject(this.#promiseResult);
} else {
try{
let x = onRejected(this.#promiseResult);
resolvePromise(newMyPromise, x, resolve, reject);
}catch(e){
reject(e);
}
}
}, 0)
} else if (this.#promiseState === PENDING) {
this.#microTaskQueue.push({
onFulfilled: () => {
setTimeout(() => {
if (typeof onFulfilled !== 'function') {
resolve(this.#promiseResult);
} else {
try{
let x = onFulfilled(this.#promiseResult);
resolvePromise(newMyPromise, x, resolve, reject);
}catch(e){
reject(e);
}
}
}, 0)
},
onRejected: () => {
setTimeout(() => {
if (typeof onRejected !== 'function') {
reject(this.#promiseResult);
} else {
try{
let x = onRejected(this.#promiseResult);
resolvePromise(newMyPromise, x, resolve, reject);
}catch(e){
reject(e);
}
}
}, 0)
}
})
}
});
return newMyPromise;
}
catch(func) {
return this.then(null, func);
}
finally(callback) {
return this.then(
// 在 Promise 成功时执行回调,并保持原 Promise 的状态和值不变
value => MyPromise.resolve(callback()).then(() => value),
// 在 Promise 失败时执行回调,并保持原 Promise 的状态和值不变
reason => MyPromise.resolve(callback()).then(() => { throw reason; })
);
}
// 下面是在Promise类上面的方法
static race([]){}
static all([]){}
static any([]){}
static allSettled([]){}
static try(f){}
}
接下来是骡子是马,跑跑测试case看一下吧
2. 测试
2.1 测试改造
我们按照官方的测试case文档,提示
resolved(value)
: creates a promise that is resolved withvalue
.rejected(reason)
: creates a promise that is already rejected withreason
.deferred()
: creates an object consisting of{ promise, resolve, reject }
:promise
is a promise that is currently in the pending state.resolve(value)
resolves the promise withvalue
.reject(reason)
moves the promise from the pending state to the rejected state, with rejection reasonreason
.
增加一下代码在最底部:
javascript
MyPromise.resolved = MyPromise.resolve;
MyPromise.rejected = MyPromise.reject;
MyPromise.deferred = function () {
let defer = {};
defer.promise = new MyPromise( (resolve, reject) => {
defer.resolve = resolve;
defer.reject = reject;
});
return defer;
}
module.exports = MyPromise;
2.2 测试配置
npm install promises-aplus-tests -g
然后修复package.json的配置
javascript
"scripts": {
// 跟上你的promise文件名称,我这里是myPromise.js
"test": "promises-aplus-tests myPromise.js --reporter spec"
// 可以将执行结果保存到文件
// "test": "promises-aplus-tests myPromise.js --reporter spec > test_report.txt"
}
执行测试:
pnpm test
or
npm run test
开始尽情测试吧
自信满满的run起来:
当然不是一次运行成功的,中间调问题也卡了很久,最终终于通过了
3. 优化
代码比较冗长,这里把重复逻辑抽取一下:
javascript
function resolvePromise(newMyPromise, x, resolve, reject, myResolve) {
// 2.3.1 如果 `promise` 和 `x` 是同一个对象的引用, 则直接将`TypeError`作为`reason`拒绝`promise`.
if (newMyPromise === x) {
reject(new TypeError('Chaining cycle'));
}
// 2.3.3如果x是一个promise,则取决于它的状态.
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else if (['[object Object]', '[object Function]'].includes(Object.prototype.toString.call(x))) {
// 2.3.3 另外,如果x是一个对象或者函数时:
try {
// 2.3.3.1 将then方法赋值为x.then
let then = x.then;
if (typeof then === 'function') {
let state = true;
try {
state = false;
// 2.3.3.3 如果then是一个函数,则将x作为它的this进行调
then.call(x, (y) => {
// 2.3.3.3.1 如果使用value y调用resolvePromise,则运行[[Resolve]](promise, y).
if (state) return;
state = true;
resolvePromise(newMyPromise, y, resolve, reject);
}, (r) => {
if (state) return;
state = true;
// 2.3.3.3.2 如果使用reason r调用 rejectPromise ,则使用r作为reason拒绝promise。
reject(r);
});
} catch (e) {
if (!state) {
// 2.3.3.3.4.2 否则 将上述的e作为 reason 拒绝promise.。
reject(e);
}
}
} else {
// 2.3.3.4 如果 then 不是一个函数,则直接使用x作为value来实现promise.
myResolve ? myResolve(x) : resolve(x);
}
} catch(e) {
// 2.3.3.2 如果读取属性x.then导致抛出异常e,则以e作为reason拒绝promise。
reject(e);
}
} else {
// 2.3.4 如果x不是一个对象或者函数,则直接使用x作为value来实现promise
myResolve ? myResolve(x) : resolve(x);
}
}
function doOnFulfilled(promise, value, resolve, reject, onFulfilled) {
if (typeof onFulfilled !== 'function') {
resolve(value);
} else {
try{
let x = onFulfilled(value);
resolvePromise(promise, x, resolve, reject);
}catch(e){
reject(e);
}
}
}
function doOnRejected(promise, value, resolve, reject, onRejected) {
if (typeof onRejected !== 'function') {
reject(value);
} else {
try{
let x = onRejected(value);
resolvePromise(promise, x, resolve, reject);
}catch(e){
reject(e);
}
}
}
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
#promiseState = PENDING;
#promiseResult = '';
#microTaskQueue = [];
constructor(executor) {
let myResolve = (value) => {
let doMyResolve = () => {
this.#promiseState = FULFILLED;
this.#promiseResult = value;
// 增加下面的逻辑
this.#microTaskQueue.forEach(item => {
item.onFulfilled(value);
});
}
if (this.#promiseState === PENDING) {
resolvePromise(this, value, myResolve, myReject, doMyResolve);
}
}
let myReject = (reason) => {
if (this.#promiseState === PENDING) {
this.#promiseState = REJECTED;
this.#promiseResult = reason;
// 增加下面的逻辑
this.#microTaskQueue.forEach(item => {
item.onRejected(reason);
});
}
}
try {
executor(myResolve, myReject);
} catch (e) {
this.#promiseState = REJECTED;
this.#promiseResult = e;
}
}
static resolve(value) {
// 参数是一个 Promise 实例
if (value instanceof MyPromise) {
return value;
}
return new MyPromise((resolve, reject) => {
resolve(value);
});
}
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
then(onFulfilled, onRejected){
let newMyPromise = new MyPromise((resolve,reject) => {
if (this.#promiseState === FULFILLED) {
setTimeout(() => {
doOnFulfilled(newMyPromise, this.#promiseResult, resolve, reject, onFulfilled);
}, 0)
} if (this.#promiseState === REJECTED) {
setTimeout(() => {
doOnRejected(newMyPromise, this.#promiseResult, resolve, reject, onRejected);
}, 0)
} else if (this.#promiseState === PENDING) {
this.#microTaskQueue.push({
onFulfilled: () => {
setTimeout(() => {
doOnFulfilled(newMyPromise, this.#promiseResult, resolve, reject, onFulfilled);
}, 0)
},
onRejected: () => {
setTimeout(() => {
doOnRejected(newMyPromise, this.#promiseResult, resolve, reject, onRejected);
}, 0)
}
})
}
});
return newMyPromise;
}
catch(func) {
return this.then(null, func);
}
finally(callback) {
return this.then(
// 在 Promise 成功时执行回调,并保持原 Promise 的状态和值不变
value => MyPromise.resolve(callback()).then(() => value),
// 在 Promise 失败时执行回调,并保持原 Promise 的状态和值不变
reason => MyPromise.resolve(callback()).then(() => { throw reason; })
);
}
// 下面是在Promise类上面的方法
static race([]){}
static all([]){}
static any([]){}
static allSettled([]){}
static try(f){}
}
MyPromise.resolved = MyPromise.resolve;
MyPromise.rejected = MyPromise.reject;
MyPromise.deferred = function () {
let defer = {};
defer.promise = new MyPromise( (resolve, reject) => {
defer.resolve = resolve;
defer.reject = reject;
});
return defer;
}
module.exports = MyPromise;
4. 遗留问题
- 关于
Promise.resolve
和构造函数这里的实现,欢迎提出意见 - 其他方法之后再实现,肝不动了