Promise/A+规范:Promises/A+
下一篇:通过手写Promise理解Promise实现原理(二)
目录
初步验证回调函数的返回值与then方法创建的Promise的关系
测试onFulfilled和onRejected中抛出异常的情况
Promise快速入门
- Promise是一种异步编程的解决方案。
- Promise是一个对象,我们通常通过构造函数创建它。
- 构造函数接收一个执行器函数(executor)作为参数。
- 执行器函数会被赋予两个函数类型的参数:resolve和reject。
- Promise有三种状态:pending、fulfilled和rejected。
- Promise的初始状态为pending。
- 在executor中调用resolve会将状态变为fulfilled,resolve接收一个参数作为Promise成功的结果。
- 在executor中调用reject,reject接收一个参数作为Promise失败的原因。
- 在executor执行过程中发生异常会将状态变为rejected,这种情况下,异常对象会作为Promise失败的原因。
- 一旦Promise的状态被改变,它的状态和结果就不能再被改变。
- Promise的结果可以是任何类型的值。
- Promise提供了一个then方法,这个方法允许我们传入两个函数类型的参数(onFulfilled和onRejected)。
- 我们可以使用then方法向Promise注册回调函数,这些回调函数可以接收当前Promise的结果。
- Promise可以多次调用then方法,所以我们可以向Promise注册多个回调函数。
- 注册的回调函数会在Promise状态改变时被推入微队列执行。
- 在Promise的状态为fulfilled时会调用onFulfilled,在Promise的状态为rejected时会调用onRejected。
- then方法的返回值也应该是一个Promise。
Promise的基本使用
前面说了这么多概念,有个大概的印象就好。现在我们先通过一个简单的例子了解如何使用Promise,之后再一点一点验证这些概念。
现在有一个定时任务,1秒钟之后会生成一个随机数,如果生成的随机数<=0.5,就输出失败;如果生成的随机数大于0.5,就输出成功。
在这个例子中,异步任务的结果是字符串"成功"或"失败",我们所需要做的后续操作只是将结果输出到控制台。
如果不使用Promise,应该是这样的:
setTimeout(() => {
if (Math.random() <= 0.5) {
console.log("失败");
} else {
console.log("成功");
}
}, 1000);
如果使用Promise,会变成这样:
new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() <= 0.5) {
reject("失败");
} else {
resolve("成功");
}
}, 1000);
}).then(
(data) => {
console.log(data);
},
(reason) => {
console.log(reason);
}
);
看上去是不使用Promise更简洁一点,在业务逻辑简单的情况下确实是这样的。但如果出现了嵌套的回调函数(内层的异步任务依赖于外层异步任务的结果)的时候,不使用Promise的可读性就很差了。当然,现在还不是考虑这些的时候,让我们先把重点放在这段代码做了些什么上:
- 首先我们通过构造函数创建了一个Promise对象,它的初始状态为pending。我们传入了执行器函数,并声明了两个形参resolve和reject用于接收传入的回调函数。在执行器函数的内部,我们定义了一个定时器任务,在任务完成时,会根据生成的随机数决定调用resolve还是reject。
- 然后我们调用了then方法,传入了两个回调函数onFulfilled和onRejected。这两个函数会被注册进callbacks数组中。
- 一秒钟过后,定时任务完成。如果生成的随机数大于0.5,调用resolve。此时Promise的状态变为fulfilled,结果变为"成功"。然后依次调用注册进callbacks数组中的onFulfilled回调函数。
- 如果生成的随机数小于等于0.5,调用reject。此时Promise的状态变为rejected,结果变为"失败"。然后依次调用注册进callbacks数组中的onRejected回调函数。
-
最后,回调函数将被传入的当前Promise的结果打印在控制台上。
初步验证执行器函数和回调函数的执行时机
根据第一个例子我们知道Promise的原理就是把回调函数封装在内部。现在让我们在第一个例子的基础上做一些改动。
const now = Date.now();
console.log("start: ", 0);
const p = new Promise((resolve, reject) => {
console.log("executor start: ", Date.now() - now);
setTimeout(() => {
if (Math.random() <= 0.5) {
reject("失败");
} else {
resolve("成功");
}
}, 1000);
});
p.then(
(data) => {
console.log("onFulfilled: ", Date.now() - now);
console.log("data: ", data);
},
(reason) => {
console.log("onRejected: ", Date.now() - now);
console.log("reason: ", reason);
}
);
setTimeout(() => {
console.log("promise settled: ", p);
}, 2000);
console.log("end: ", Date.now() - now);
在浏览器环境下输出的结果为:
start: 0
executor start: 0
end: 0
onRejected: 1006
reason: 失败
promise settled: Promise {<rejected>: '失败'}
根据输出的结果,我们可以确定:
- 传入的执行器函数是同步执行的。
- 如果调用then方法时,Promise的状态为pending,回调函数并不会立即执行,只有Promise状态改变后,回调函数才会执行。在本例中,一秒后Promise的状态变为rejected,失败的原因为"失败",然后调用onRejected回调函数。
初步验证回调函数的返回值与then方法创建的Promise的关系
我们知道传入then方法的onFulfilled和onRejected会注册进then方法的调用者的callbacks,且调用then方法会创建一个新的Promise对象。那么回调函数的返回值与新的Promise的状态和结果是否存在某种关系呢?
这次我们依旧在第一个例子的基础上做一些改动。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() <= 0.5) {
reject("失败");
} else {
resolve("成功");
}
}, 1000);
});
console.log("p1: ", p1);
const p2 = p1.then(
(data) => {
console.log("p2 onFulfilled ", p2);
return 666;
},
(reason) => {
console.log("p2 onRejected ", p2);
return 888;
}
);
console.log("p2: ", p2);
const p3 = p1.then(
(data) => {
console.log("p3 onFulfilled ", p3);
},
(reason) => {
console.log("p3 onRejected ", p3);
}
);
console.log("p3: ", p3);
const p4 = p1.then(
(data) => {
console.log("p4 onFulfilled ", p4);
return { name: "Tom", age: 18 };
},
(reason) => {
console.log("p4 onRejected ", p4);
return { name: "Jerry", age: 8 };
}
);
console.log("p4: ", p4);
const p5 = p1.then(
(data) => {
console.log("p5 onFulfilled ", p5);
return new Error(123);
},
(reason) => {
console.log("p5 onRejected ", p5);
return new Error(456);
}
);
console.log("p5: ", p5);
setTimeout(() => {
console.log("p1 settled: ", p1);
console.log("p2 settled: ", p2);
console.log("p3 settled: ", p3);
console.log("p4 settled: ", p4);
console.log("p5 settled: ", p5);
}, 2000);
当p1成功时,输出的结果为:
p1: Promise {<pending>}
p2: Promise {<pending>}
p3: Promise {<pending>}
p4: Promise {<pending>}
p5: Promise {<pending>}
p2 onFulfilled Promise {<pending>}
p3 onFulfilled Promise {<pending>}
p4 onFulfilled Promise {<pending>}
p5 onFulfilled Promise {<pending>}
p1 settled: Promise {<fulfilled>: '成功'}
p2 settled: Promise {<fulfilled>: 666}
p3 settled: Promise {<fulfilled>: undefined}
p4 settled: Promise {<fulfilled>: {...}}
p5 settled: Promise {<fulfilled>: Error: 123}
当p1失败时,输出的结果为:
p1: Promise {<pending>}
p2: Promise {<pending>}
p3: Promise {<pending>}
p4: Promise {<pending>}
p5: Promise {<pending>}
p2 onRejected Promise {<pending>}
p3 onRejected Promise {<pending>}
p4 onRejected Promise {<pending>}
p5 onRejected Promise {<pending>}
p1 settled: Promise {<rejected>: '失败'}
p2 settled: Promise {<fulfilled>: 888}
p3 settled: Promise {<fulfilled>: undefined}
p4 settled: Promise {<fulfilled>: {...}}
p5 settled: Promise {<fulfilled>: Error: 456}
根据输出的结果,我们可以确定:
- 在回调函数开始执行时,then方法返回的Promise函数依然是pending状态,Promise的状态改变起码发生在回调函数执行完成之后。
- 在明确传了对应回调函数的情况下,不管调用的是onFulfilled还是onRejected,不管回调函数返回的是一个基本类型(如数字)、一个普通对象、一个错误对象还是没有返回值(等价于返回undefined),只要回调函数执行期间没有出现异常,返回的Promise对象的状态都会变成fulfilled,且Promise的结果为回调函数的返回值。
第一版实现
class Promise {
constructor(executor) {
this.state = "pending";
this.value = null;
this.handlers = [];
executor(this._resolve.bind(this), this._reject.bind(this));
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
this._pushHandler(onFulfilled, onRejected, resolve, reject);
});
}
_resolve(data) {
this.state = "fulfilled";
this.value = data;
this._executeHandlers();
}
_reject(reason) {
this.state = "rejected";
this.value = reason;
this._executeHandlers();
}
_pushHandler(onFulfilled, onRejected, resolve, reject) {
this.handlers.push({
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject,
});
}
_executeHandlers() {
if (this.state === "pending") {
return;
}
this.handlers.forEach((handler) => this._handle(handler));
}
_handle(handler) {
const callback =
this.state === "fulfilled" ? handler.onFulfilled : handler.onRejected;
const result = callback(this.value);
handler.resolve(result);
}
}
根据已知的信息,首先我们需要:
- 定义一个属性value,用于保存Promise的结果,它的初始值可以为null或undefined。
- 定义一个属性state,用于保存Promise当前的状态,它的初始值应该是"pending"。
- 定义一个属性handlers,它是一个数组,我准备将then方法注册的onFulfilled和onRejected打包成一个对象放进数组中,当然这不是必须的。
然后我们需要定义两个内部方法_resolve和_reject,用于改变状态、保存结果和执行回调;并在调用执行器函数时将这两个函数作为参数传入。因为执行器函数是同步执行的,我们直接在构造函数中调用它。
因为then方法会返回一个Promise,我们先返回一个新的Promise。
接下来是注册回调和执行回调:
- 在then方法中注册回调:因为同一个Promise对象可以多次调用then方法注册多个回调函数,所以用于保存这些回调的必须是一个数组。我们将onFulfilled、onRejected、resolve和reject打包进一个对象放入数组,这里的resolve和reject已经处理过this指向问题了所以不用再处理。
- 在resolve和reject中执行回调:在Promise状态改变后,可以根据当前Promise的状态决定调用handlers中注册的onFulfilled或onRejected。因为传入回调时还顺带传入了新Promise对象的resolve和reject方法,所以我们可以在执行回调之后改变新Promise对象的状态和结果。在之前的例子中,回调执行后,then方法返回的Promise的状态都会变为fulfilled,那我们就先统一调用resolve方法。
可以先跑一下之前的几个例子看看结果:
start: 0
executor start: 0
end: 0
onRejected: 1007
reason: 失败
promise settled: Promise {state: 'rejected', value: '失败', handlers: Array(1)}
p1: Promise {state: 'pending', value: null, handlers: Array(0)}
p2: Promise {state: 'pending', value: null, handlers: Array(0)}
p3: Promise {state: 'pending', value: null, handlers: Array(0)}
p4: Promise {state: 'pending', value: null, handlers: Array(0)}
p5: Promise {state: 'pending', value: null, handlers: Array(0)}
p2 onFulfilled Promise {state: 'pending', value: null, handlers: Array(0)}
p3 onFulfilled Promise {state: 'pending', value: null, handlers: Array(0)}
p4 onFulfilled Promise {state: 'pending', value: null, handlers: Array(0)}
p5 onFulfilled Promise {state: 'pending', value: null, handlers: Array(0)}
p1 settled: Promise {state: 'fulfilled', value: '成功', handlers: Array(4)}
p2 settled: Promise {state: 'fulfilled', value: 666, handlers: Array(0)}
p3 settled: Promise {state: 'fulfilled', value: undefined, handlers: Array(0)}
p4 settled: Promise {state: 'fulfilled', value: {...}, handlers: Array(0)}
p5 settled: Promise {state: 'fulfilled', value: Error: 123, handlers: Array(0)}
测试在执行器函数中多次改变Promise状态
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
console.log("after resolve 1...");
resolve(2);
console.log("after resolve 2...");
}, 1000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(3);
console.log("after reject 3...");
resolve(4);
console.log("after resolve 4...");
}, 1000);
});
setTimeout(() => {
console.log("p1: ", p1);
console.log("p2: ", p2);
}, 2000);
输出结果如下:
after resolve 1...
after resolve 2...
after reject 3...
after resolve 4...
Uncaught (in promise) 3
p1: Promise {<fulfilled>: 1}
p2: Promise {<rejected>: 3}
根据输出结果我们可以确定,在Promise状态改变后:
- 后续的代码仍会执行
- 它的结果和状态就确定了,后续任何改变状态或结果的尝试都不会生效
为此,我们需要对第一版实现做一些小改动。
class Promise {
// ...
_changeState(newState, value) {
if (this.state !== "pending") {
return;
}
this.state = newState;
this.value = value;
this._executeHandlers();
}
_resolve(data) {
this._changeState("fulfilled", data);
}
_reject(reason) {
this._changeState("rejected", reason);
}
// ...
}
测试在执行器函数中同步改变Promise状态
const p = new Promise((resolve, reject) => {
console.log("同步执行");
resolve("成功");
});
p.then((data) => {
console.log("data: ", data);
});
setTimeout(() => {
p.then((data) => {
console.log("timeout: ", data);
});
}, 1000);
之前我们都是在定时器的回调方法中调用resolve或reject方法,然后一次性执行所有已经注册的回调函数。当我们通过then方法注册回调时,Promise对象的状态还没有改变,那如果在执行器函数同步执行时就已经改变了Promise的状态呢?输出结果如下:
同步执行
data: 成功
timeout: 成功
而以我们的第一版实现,只能打印出第一行的同步执行:
同步执行
想要解决这个问题, 只需要在调用then方法时也执行一下已经注册的回调就行了。为了避免重复执行回调函数,我选择再注册的时候加一个标记位记录该回调函数是否已经被执行了。
第二版实现
class Promise {
constructor(executor) {
this.state = "pending";
this.value = null;
this.handlers = [];
executor(this._resolve.bind(this), this._reject.bind(this));
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
this._pushHandler(onFulfilled, onRejected, resolve, reject);
// 改动1
this._executeHandlers();
});
}
_changeState(newState, value) {
if (this.state !== "pending") {
return;
}
this.state = newState;
this.value = value;
this._executeHandlers();
}
_resolve(data) {
this._changeState("fulfilled", data);
}
_reject(reason) {
this._changeState("rejected", reason);
}
_pushHandler(onFulfilled, onRejected, resolve, reject) {
this.handlers.push({
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject,
// 改动2
handled: false,
});
}
_executeHandlers() {
if (this.state === "pending") {
return;
}
this.handlers.forEach((handler) => this._handle(handler));
}
_handle(handler) {
// 改动3
if (handler.handled === true) {
return;
}
const callback =
this.state === "fulfilled" ? handler.onFulfilled : handler.onRejected;
const result = callback(this.value);
handler.resolve(result);
handler.handled = true;
}
}
测试回调函数返回值为Promise的情况
之前只测试了回调函数的返回值是非Promise对象的情况,现在让我们看看如果一个回调函数返回Promise会怎么样。
const now = Date.now();
const p1 = new Promise((resolve, reject) => {
resolve(1);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2);
}, 1000);
});
const p3 = p1.then((data) => {
return p2;
});
const p4 = p3.then((data) => {
console.log(Date.now() - now);
console.log("p2: ", p2);
console.log("p3: ", p3);
console.log("p2 === p3", p2 === p3);
console.log("data: ", data);
});
输出结果为:
1006
p2: Promise {<fulfilled>: 2}
p3: Promise {<fulfilled>: 2}
p2 === p3 false
data: 2
根据这个例子我们可以知道:如果回调函数返回一个Promise,注册这个回调函数的then方法返回的Promise应该与回调函数返回的Promise保持一致。在本例中,就是p3应该与p2的状态保持一致。按照v8的实现方式,如果返回的是一个Promise,应该要调用这个Promise的then方法,把resolve和reject作为参数传入。
我们需要一个判断是否是Promise的辅助函数,如下:
function isPromise(value) {
return (
!!value &&
(typeof value === "object" || typeof value === "function") &&
typeof value.then === "function"
);
}
为了满足A+规范的互操作性,只需要传入的对象满足A+规范,那它就是一个Promise。而不是必须是一个ES6的Promise对象,所以这里不应该用instanceof Promise判断。
目前我们在then方法返回时只调用resolve,那就在_resolve中处理返回值为Promise的情况。
_resolve(data) {
if (isPromise(data)) {
data.then.call(
data,
this._resolve.bind(this),
this._reject.bind(this)
);
return;
}
this._changeState("fulfilled", data);
}
测试一下:
1001
p2: Promise {state: <fulfilled>, value: 2, handlers: Array(1)}
p3: Promise {state: <fulfilled>, value: 2, handlers: Array(1)}
p2 === p3 false
data: 2
注意这两个handlers虽然都是Array(1)但里面的回调函数是不一样的哦:
- p2的handlers里面是p3的resolve和reject。
- p3的handlers则是通过p3.then注册的回调函数。
测试执行器函数中抛出异常的情况
const p1 = new Promise((resolve, reject) => {
throw new Error("1");
resolve("2");
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
throw new Error("3");
resolve("4");
}, 1000);
});
const p3 = new Promise((resolve, reject) => {
resolve("5");
throw new Error("6");
});
const p4 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error("7"));
}, 1000);
});
const p5 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("8");
throw new Error("9");
}, 1000);
});
setTimeout(() => {
console.log("p1: ", p1);
console.log("p2: ", p2);
console.log("P3: ", p3);
console.log("p4: ", p4);
console.log("p5: ", p5);
}, 2000);
我们可以看到,控制台首先输出了:
Uncaught (in promise)
Error: 1
一秒后,依次输出:
Uncaught
Error: 3
Uncaught
Error: 9
Uncaught (in promise)
Error: 7
两秒后,输出:
p1: Promise {<rejected>: Error: 1}
p2: Promise {<pending>}
p3: Promise {<fulfilled>: '5'}
p4: Promise {<rejected>: Error: 7}
p5: Promise {<fulfilled>: '8'}
首先我们要知道 Uncaught (in promise) 是浏览器控制台输出的警告消息,它表示在Promise执行过程中发生了一个未捕获的异常。它通常出现在当Promise被reject,且没有对应的onRejected来处理这个rejection。
让我们先一个一个分析:
- p1的执行器函数同步执行时抛出异常,Promise的状态变为rejected。由于Promise的状态已经改变,后续的resolve("2")不会生效。在Promise状态检查时发现没有对应的onRejected来处理这个rejection,于是输出了 Uncaught (in promise) 。
- p2的执行器函数同步执行时创建了一个定时任务,一秒之后定时任务完成,将回调函数推入队列。在setTimeout回调函数中首先抛出一个错误,因为它是发生在事件循环的一个完全独立的阶段,所以这个异常无法被Promise捕获处理。抛出异常之后回调函数中止执行,所以p2的状态没有发生改变。
- p3的执行器函数同步执行时,首先将Promise的状态变为resolve。之后立即抛出一个异常,这个异常会被Promise捕获,所以不会输出 Uncaught (in promise) 。捕获异常后Promise会尝试将状态变为reject,但此时Promise的状态已经改变,所以改变状态的尝试不会生效。
- p4在setTimeout回调函数中调用reject,由于没有对应的onRejected,会输出 Uncaught (in promise)。之所以先输出 Error: 3 和 Error: 9 后输出Error: 7,是因为前者是在回调函数中直接抛出的异常,后者是在回调函数结束周,Promise状态检查时抛出的警告。
- p5和p2类似,区别在于setTimeout回调函数中先调用resolve改变了Promise的状态,再抛出异常。
如果我们确定在执行setTimeout回调函数时可能会抛出异常,可以这么处理:
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
throw new Error("3");
} catch (error) {
reject(error);
}
resolve("4");
}, 1000);
});
测试onFulfilled和onRejected中抛出异常的情况
const p1 = new Promise((resolve, reject) => {
resolve("2");
});
const p2 = p1.then(() => {
throw new Error(123);
});
setTimeout(() => {
console.log("p1: ", p1);
console.log("p2: ", p2);
}, 2000);
输出结果如下:
Uncaught (in promise)
Error: 123
p1: Promise {<fulfilled>: '2'}
p2: Promise {<rejected>: Error: 123}
之前不管是调用Promise的onFulfilled还是onRejected,只要是正确的执行了回调函数且返回值不为Promise,都会调用resolve使状态变为fulfilled。现在我们知道在执行中抛出异常是会调用reject使状态变为rejected的。
第三版实现
function isPromise(value) {
return (
!!value &&
(typeof value === "object" || typeof value === "function") &&
typeof value.then === "function"
);
}
class Promise {
constructor(executor) {
this.state = "pending";
this.value = null;
this.handlers = [];
try {
executor(this._resolve.bind(this), this._reject.bind(this));
} catch (error) {
console.error(error);
this._reject(error);
}
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
this._pushHandler(onFulfilled, onRejected, resolve, reject);
this._executeHandlers();
});
}
_changeState(newState, value) {
if (this.state !== "pending") {
return;
}
this.state = newState;
this.value = value;
this._executeHandlers();
}
_resolve(data) {
if (isPromise(data)) {
data.then.call(
data,
this._resolve.bind(this),
this._reject.bind(this)
);
return;
}
this._changeState("fulfilled", data);
}
_reject(reason) {
this._changeState("rejected", reason);
}
_pushHandler(onFulfilled, onRejected, resolve, reject) {
this.handlers.push({
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject,
handled: false,
});
}
_executeHandlers() {
if (this.state === "pending") {
return;
}
this.handlers.forEach((handler) => this._handle(handler));
}
_handle(handler) {
if (handler.handled === true) {
return;
}
const callback =
this.state === "fulfilled" ? handler.onFulfilled : handler.onRejected;
try {
const result = callback(this.value);
handler.resolve(result);
} catch (error) {
console.error(error);
handler.reject(error);
} finally {
handler.handled = true;
}
}
}
测试不传onFulfilled或onRejected
根据A+规范,onFulfilled和onRejected都是可选的,如果onFulfilled或onRejected不是一个函数,应该忽略它。
const p1 = new Promise((resolve, reject) => {
reject(123);
});
const p2 = p1.then((data) => {
console.log(data);
});
const p3 = p2.then((data) => {
console.log(data);
}, 123);
const p4 = p3.then(
(data) => {
console.log(data);
},
(reason) => {
console.log(reason);
}
);
setTimeout(() => {
console.log("p1: ", p1);
console.log("p2: ", p2);
console.log("p3: ", p3);
console.log("p4: ", p4);
});
输出结果:
123
p1: Promise {<rejected>: 123}
p2: Promise {<rejected>: 123}
p3: Promise {<rejected>: 123}
p4: Promise {<fulfilled>: undefined}
也就是说,如果一个Promise调用了resolve或reject,但是没有被对应的回调函数处理,状态和结果会被传递到链的下一步:
_handle(handler) {
if (handler.handled === true) {
return;
}
try {
let result;
if (this.state === "fulfilled") {
result =
typeof handler.onFulfilled === "function"
? handler.onFulfilled(this.value)
: this.value;
handler.resolve(result);
} else if (this.state === "rejected") {
if (typeof handler.onRejected === "function") {
result = handler.onRejected(this.value);
handler.resolve(result);
} else {
handler.reject(this.value);
}
}
} catch (error) {
console.error(error);
handler.reject(error);
} finally {
handler.handled = true;
}
}
模拟微任务
then方法注册的回调是会放到微队列执行的,我们想要模拟的话,可以使用MutationObserver创建一个微任务。
function queueMicrotaskPolyfill(callback) {
let textNode = document.createTextNode("");
let counter = 0;
const observer = new MutationObserver(() => {
callback();
observer.disconnect();
});
observer.observe(textNode, { characterData: true });
textNode.data = String(counter++);
}
第四版实现
function isPromise(value) {
return (
!!value &&
(typeof value === "object" || typeof value === "function") &&
typeof value.then === "function"
);
}
function queueMicrotaskPolyfill(callback) {
let textNode = document.createTextNode("");
let counter = 0;
const observer = new MutationObserver(() => {
callback();
observer.disconnect();
});
observer.observe(textNode, { characterData: true });
textNode.data = String(counter++);
}
class Promise {
constructor(executor) {
this.state = "pending";
this.value = null;
this.handlers = [];
try {
executor(this._resolve.bind(this), this._reject.bind(this));
} catch (error) {
console.error(error);
this._reject(error);
}
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
this._pushHandler(onFulfilled, onRejected, resolve, reject);
this._executeHandlers();
});
}
_changeState(newState, value) {
if (this.state !== "pending") {
return;
}
this.state = newState;
this.value = value;
this._executeHandlers();
}
_resolve(data) {
if (isPromise(data)) {
data.then.call(
data,
this._resolve.bind(this),
this._reject.bind(this)
);
return;
}
this._changeState("fulfilled", data);
}
_reject(reason) {
this._changeState("rejected", reason);
}
_pushHandler(onFulfilled, onRejected, resolve, reject) {
this.handlers.push({
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject,
handled: false,
});
}
_executeHandlers() {
if (this.state === "pending") {
return;
}
this.handlers.forEach((handler) => this._handle(handler));
}
_handle(handler) {
if (handler.handled === true) {
return;
}
queueMicrotaskPolyfill(() => {
try {
let result;
if (this.state === "fulfilled") {
result =
typeof handler.onFulfilled === "function"
? handler.onFulfilled(this.value)
: this.value;
handler.resolve(result);
} else if (this.state === "rejected") {
if (typeof handler.onRejected === "function") {
result = handler.onRejected(this.value);
handler.resolve(result);
} else {
handler.reject(this.value);
}
}
} catch (error) {
console.error(error);
handler.reject(error);
} finally {
handler.handled = true;
}
});
}
}
至此,一个满足A+规范的Promise就算是基本完成了,虽然有很多细节还不完善。至于catch、finally、resolve、reject等等其实并不在A+规范的要求内。