手写promise
https://www.bilibili.com/video/BV1ug411w7sb (opens new window)
Promise.all;
Promise.all 是静态方法,不能写在 prototype 上,如果是 then,catch 等实例方法,要写在 promise.prototype 上
用类写的话,直接 static
if (promise instanceof Promise) {
}
这个判断可以省略掉,直接写成
Promise.resolve(promise);
这样不管是常量还是 promise 都包装成了 Promise
# Promise/A+规范
官方的地址为:https://promisesaplus.com/ (opens new window)
# 林三心版本 Promise
https://juejin.cn/post/6994594642280857630 (opens new window)
class MyPromise {
constructor(executor) {
this.initValue();
this.initBind();
try {
executor(this.resolve, this.reject);
} catch (error) {
this.reject(error);
}
}
initValue() {
this.PromiseState = "pending";
this.PromiseResult = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
}
initBind() {
this.resolve = this.resolve.bind(this);
this.reject = this.reject.bind(this);
}
resolve(value) {
if (this.PromiseState !== "pending") return;
this.PromiseState = "fulfilled";
this.PromiseResult = value;
while (this.onFulfilledCallbacks.length) {
this.onFulfilledCallbacks.shift()(this.PromiseResult);
}
}
reject(reason) {
if (this.PromiseState !== "pending") return;
this.PromiseState = "rejected";
this.PromiseResult = reason;
while (this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(this.PromiseResult);
}
}
then(onFulfilled, onRejected) {
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (val) => val;
onRejected =
typeof onRejected === "function"
? onRejected
: (reason) => {
throw new Error(reason);
};
var thenPromise = new MyPromise((resolve, reject) => {
const resolvePromise = (cb) => {
queueMicrotask(() => {
try {
const x = cb(this.PromiseResult);
if (x === thenPromise) {
throw new Error("不能返回自身");
}
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
} catch (error) {
reject(error);
}
});
};
if (this.PromiseState === "fulfilled") {
resolvePromise(onFulfilled);
} else if (this.PromiseState === "rejected") {
resolvePromise(onRejected);
} else if (this.PromiseState === "pending") {
this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled));
this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected));
}
});
return thenPromise;
}
// 语法糖
finally(cb) {
return then(
(res) => MyPromise.resolve(cb()).then(() => res),
(err) => MyPromise.resolve(cb()).then(() => throw err)
);
}
catch(onRejected) {
return this.then(null, onRejected);
}
static all(promises) {
return new MyPromise((resolve, reject) => {
let result = [];
// 这里使用count是为了后面用length判断会失误,比如直接result[index]这里index如果是length,前面的结果并没有输入,而是undefined,就会错误
let count = 0;
let addData = (value, index) => {
count++;
// 这里要用index,不能用push,push会导致顺序混乱
result[index] = value;
// 用count计数,不能用数组长度判断
if (count === promises.length) {
resolve(result);
}
};
promises.forEach((promise) => {
// 这里的判断可以改成Promise.resolve()无论里面是不是常量都会被包成Promise
if (promise instanceof MyPromise) {
promise.then(
(res) => {
addData(res);
},
(err) => {
reject(err);
}
);
} else {
addData(res);
}
});
});
}
static race(promises) {
return new Promise((resolve, reject) => {
promises.forEach((promise) => {
Promise.resolve(promise).then(
(res) => {
resolve(res);
},
(err) => {
reject(err);
}
);
// if (promise instanceof Promise) {
// promise.then(
// (res) => {
// resolve(res);
// },
// (err) => {
// rejecte(err);
// }
// );
// } else {
// resolve(promise);
// }
});
});
}
static allSettled(promises) {
return new MyPromise((resolve, reject) => {
const result = [];
const addData = (status, value, index) => {
result[index] = {
status,
value,
};
if (result.length === promises.length) {
resolve(result);
}
};
promises.forEach((promise, index) => {
if (promise instanceof MyPromise) {
promise.then(
(res) => {
addData("fulfilled", res, index);
},
(err) => {
addData("rejected", err, index);
}
);
} else {
addData("rejected", promise, index);
}
});
});
}
static any(promises) {
return new MyPromise((resolve, reject) => {
let count = 0;
promises.forEach((promise) => {
promise.then(
(res) => {
resolve(res);
},
(err) => {
count++;
if (count === promises.length) {
reject(new AggregateError("All promises were rejected"));
}
}
);
});
});
}
}
const test2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject(222);
}, 2000);
});
const test = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject(111);
}, 1000);
});
const test3 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject(333);
}, 3000);
});
// Promise.race([test2, test, test3]).then(console.log);
// MyPromise.race([test2, test, test3]).then(console.log);
// Promise.all([test2, test, test3]).then(console.log, console.error);
// MyPromise.all([test2, test, test3]).then(console.log, console.error);
// Promise.allSettled([test2, test, test3]).then(console.log, console.error);
// MyPromise.allSettled([test2, test, test3]).then(console.log, console.error);
Promise.any([test2, test, test3]).then(console.log, console.error);
MyPromise.any([test2, test, test3]).then(console.log, console.error);
# 术语
“promise” is an object or function with a then method whose behavior conforms to this specification. “thenable” is an object or function that defines a then method. “value” is any legal JavaScript value (including undefined, a thenable, or a promise). “exception” is a value that is thrown using the throw statement. “reason” is a value that indicates why a promise was rejected.
“promise”:是一个具有 then 方法的对象或者函数,它的行为符合该规范。
“thenable”:是一个定义了 then 方法的对象或者函数。
“value”:可以是任何一个合法的 JavaScript 的值(包括 undefined、thenable 或 promise)。
“exception”:是一个异常,是在 Promise 里面可以用 throw 语句抛出来的值。
“reason”:是一个 Promise 里 reject 之后返回的拒绝原因。
# 状态描述
A promise must be in one of three states: pending, fulfilled, or rejected. When pending, a promise: may transition to either the fulfilled or rejected state. When fulfilled, a promise: must not transition to any other state. must have a value, which must not change. When rejected, a promise: must not transition to any other state. must have a reason, which must not change. Here, “must not change” means immutable identity (i.e. ===), but does not imply deep immutability.
一个 Promise 有三种状态:pending、fulfilled 和 rejected。
当状态为 pending 状态时,即可以转换为 fulfilled 或者 rejected 其中之一
当状态为 fulfilled 状态时,就不能转换为其他状态了,必须返回一个不能再改变的值
当状态为 rejected 状态时,同样也不能转换为其他状态,必须有一个原因的值也不能改变
# then 方法
一个 Promise 必须拥有一个 then 方法来访问它的值或者拒绝原因
then 方法有两个参数
promise.then(onFulfilled, onRejected)
onFulfilled 和 onRejected 特性
如果 onFulfilled 是函数,则当 Promise 执行结束之后必须被调用,最终返回值为 value,其调用次数不可超过一次。而 onRejected 除了最后返回的是 reason 外,其他方面和 onFulfilled 在规范上的表述基本一样
# 多次调用
then 方法其实可以被一个 Promise 调用多次,且必须返回一个 Promise 对象。then 的写法如下所示,其中 Promise1 执行了 then 的方法之后,返回的依旧是个 Promise2,然后我们拿着 Promise2 又可以执行 then 方法,而 Promise2 是一个新的 Promise 对象,又可以继续进行 then 方法调用
promise2 = promise1.then(onFulfilled, onRejected);
# 一步步实现 Promise
按照 Promise/A+ 的规范,第一步就是构造函数
# 构造函数
Promise 构造函数接受一个 executor 函数,executor 函数执行完同步或者异步操作后,调用它的两个参数 resolve 和 reject
function Promise(executor) {
var self = this;
self.status = "pending"; // Promise当前的状态
self.data = undefined; // Promise的值
self.onResolvedCallback = []; // Promise resolve时的回调函数集
self.onRejectedCallback = []; // Promise reject时的回调函数集
executor(resolve, reject); // 执行executor并传入相应的参数
}
从上面的代码中可以看出,我们先定义了一个 Promise 的初始状态 pending,以及参数执行函数 executor,并且按照规范设计了一个 resolve 回调函数集合数组 onResolvedCallback 以及 一个 reject 回调函数集合数组,那么构造函数的初始化就基本完成了
接下来我们看看还需要添加什么东西呢?那就是需要在构造函数中完善 resolve 和 reject 两个函数,完善之后的代码如下
function Promise(executor) {
var self = this;
self.status = "pending"; // Promise当前的状态
self.data = undefined; // Promise的值
self.onResolvedCallback = []; // Promise resolve时的回调函数集
self.onRejectedCallback = []; // Promise reject时的回调函数集
function resolve(value) {
// TODO
}
function reject(reason) {
// TODO
}
try {
// 考虑到执行过程中有可能出错,所以我们用try/catch块给包起
executor(resolve, reject); // 执行executor
} catch (e) {
reject(e);
}
}
resolve 和 reject 内部应该怎么实现呢?我们根据规范知道这两个方法主要做的事情就是返回对应状态的值 value 或者 reason,并把 Promise 内部的 status 从 pending 变成对应的状态,并且这个状态在改变了之后是不可以逆转的
function Promise(executor) {
// ...上面的省略
function resolve(value) {
if (self.status === "pending") {
self.status = "resolved";
self.data = value;
for (var i = 0; i < self.onResolvedCallback.length; i++) {
self.onResolvedCallback[i](value);
}
}
}
function reject(reason) {
if (self.status === "pending") {
self.status = "rejected";
self.data = reason;
for (var i = 0; i < self.onRejectedCallback.length; i++) {
self.onRejectedCallback[i](reason);
}
}
}
// 下面的省略
}
上述代码所展示的,基本就是在判断状态为 pending 之后,把状态改为相应的值,并把对应的 value 和 reason 存在内部的 data 属性上面,之后执行相应的回调函数
# 实现 then 方法
then 方法是 Promise 执行完之后可以拿到 value 或者 reason 的方法,并且还要保持 then 执行之后,返回的依旧是一个 Promise 方法,还要支持多次调用
// then方法接收两个参数onResolved和onRejected,分别为Promise成功或失败后的回调
Promise.prototype.then = function (onResolved, onRejected) {
var self = this;
var promise2;
// 根据标准,如果then的参数不是function,则需要忽略它
onResolved = typeof onResolved === "function" ? onResolved : function (v) {};
onRejected = typeof onRejected === "function" ? onRejected : function (r) {};
if (self.status === "resolved") {
return (promise2 = new Promise(function (resolve, reject) {}));
}
if (self.status === "rejected") {
return (promise2 = new Promise(function (resolve, reject) {}));
}
if (self.status === "pending") {
return (promise2 = new Promise(function (resolve, reject) {}));
}
};
从上面的代码中可以看到,我们在 then 方法内部先初始化了 Promise2 的对象,用来存放执行之后返回的 Promise,并且还需要判断 then 方法传参进来的两个参数必须为函数,这样才可以继续执行。
上面我只是搭建了 then 方法框架的整体思路,但是不同状态的返回细节处理也需要完善,通过仔细阅读标准,完善之后的 then 的代码如下
Promise.prototype.then = function (onResolved, onRejected) {
var self = this;
var promise2;
// 根据标准,如果then的参数不是function,则需要忽略它
onResolved =
typeof onResolved === "function" ? onResolved : function (value) {};
onRejected =
typeof onRejected === "function" ? onRejected : function (reason) {};
if (self.status === "resolved") {
// 如果promise1的状态已经确定并且是resolved,我们调用onResolved,考虑到有可能throw,所以还需要将其包在try/catch块里
return (promise2 = new Promise(function (resolve, reject) {
try {
var x = onResolved(self.data);
if (x instanceof Promise) {
// 如果onResolved的返回值是一个Promise对象,直接取它的结果作为promise2的结果
x.then(resolve, reject);
}
resolve(x); // 否则,以它的返回值作为promise2的结果
} catch (e) {
reject(e); // 如果出错,以捕获到的错误作为promise2的结果
}
}));
}
// 此处与前一个if块的逻辑几乎相同,区别在于所调用的是onRejected函数
if (self.status === "rejected") {
return (promise2 = new Promise(function (resolve, reject) {
try {
var x = onRejected(self.data);
if (x instanceof Promise) {
x.then(resolve, reject);
}
} catch (e) {
reject(e);
}
}));
}
if (self.status === "pending") {
// 如果当前的Promise还处于pending状态,我们并不能确定调用onResolved还是onRejected,只能等到Promise的状态确定后,才能确定如何处理
return (promise2 = new Promise(function (resolve, reject) {
self.onResolvedCallback.push(function (value) {
try {
var x = onResolved(self.data);
if (x instanceof Promise) {
x.then(resolve, reject);
}
} catch (e) {
reject(e);
}
});
self.onRejectedCallback.push(function (reason) {
try {
var x = onRejected(self.data);
if (x instanceof Promise) {
x.then(resolve, reject);
}
} catch (e) {
reject(e);
}
});
}));
}
};
根据上面的代码可以看出,我们基本实现了一个符合标准的 then 方法。但是标准里提到了,还要支持不同的 Promise 进行交互,关于不同的 Promise 交互其实 Promise 标准说明(https://promisesaplus.com/#point-46 (opens new window))中有提到。其中详细指定了如何通过 then 的实参返回的值来决定 Promise2 的状态
关于为何需要不同的 Promise 实现交互,原因应该是 Promise 并不是 JS 一开始存在的标准,如果你使用的某一个库中封装了一个 Promise 的实现,想象一下如果它不能跟你自己使用的 Promise 实现交互的情况,其实还是会有问题的,因此我们还需要调整一下 then 方法中执行 Promise 的方法
另外还有一个需要注意的是,在 Promise/A+ 规范中,onResolved 和 onRejected 这两项函数需要异步调用,关于这一点,标准里面是这么说的
In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack.
所以我们需要对代码做一点变动,即在处理 Promise 进行 resolve 或者 reject 的时候,加上 setTimeout(fn, 0)
# 完整版的代码
try {
module.exports = Promise;
} catch (e) {}
function Promise(executor) {
var self = this;
self.status = "pending";
self.onResolvedCallback = [];
self.onRejectedCallback = [];
function resolve(value) {
if (value instanceof Promise) {
return value.then(resolve, reject);
}
setTimeout(function () {
// 异步执行所有的回调函数
if (self.status === "pending") {
self.status = "resolved";
self.data = value;
for (var i = 0; i < self.onResolvedCallback.length; i++) {
self.onResolvedCallback[i](value);
}
}
});
}
function reject(reason) {
setTimeout(function () {
// 异步执行所有的回调函数
if (self.status === "pending") {
self.status = "rejected";
self.data = reason;
for (var i = 0; i < self.onRejectedCallback.length; i++) {
self.onRejectedCallback[i](reason);
}
}
});
}
try {
executor(resolve, reject);
} catch (reason) {
reject(reason);
}
}
function resolvePromise(promise2, x, resolve, reject) {
var then;
var thenCalledOrThrow = false;
if (promise2 === x) {
return reject(new TypeError("Chaining cycle detected for promise!"));
}
if (x instanceof Promise) {
if (x.status === "pending") {
x.then(function (v) {
resolvePromise(promise2, v, resolve, reject);
}, reject);
} else {
x.then(resolve, reject);
}
return;
}
if (x !== null && (typeof x === "object" || typeof x === "function")) {
try {
then = x.then;
if (typeof then === "function") {
then.call(
x,
function rs(y) {
if (thenCalledOrThrow) return;
thenCalledOrThrow = true;
return resolvePromise(promise2, y, resolve, reject);
},
function rj(r) {
if (thenCalledOrThrow) return;
thenCalledOrThrow = true;
return reject(r);
}
);
} else {
resolve(x);
}
} catch (e) {
if (thenCalledOrThrow) return;
thenCalledOrThrow = true;
return reject(e);
}
} else {
resolve(x);
}
}
Promise.prototype.then = function (onResolved, onRejected) {
var self = this;
var promise2;
onResolved =
typeof onResolved === "function"
? onResolved
: function (v) {
return v;
};
onRejected =
typeof onRejected === "function"
? onRejected
: function (r) {
throw r;
};
if (self.status === "resolved") {
return (promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
// 异步执行onResolved
try {
var x = onResolved(self.data);
resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
});
}));
}
if (self.status === "rejected") {
return (promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
// 异步执行onRejected
try {
var x = onRejected(self.data);
resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
});
}));
}
if (self.status === "pending") {
// 这里之所以没有异步执行,是因为这些函数必然会被resolve或reject调用,而resolve或reject函数里的内容已是异步执行,构造函数里的定义
return (promise2 = new Promise(function (resolve, reject) {
self.onResolvedCallback.push(function (value) {
try {
var x = onResolved(value);
resolvePromise(promise2, x, resolve, reject);
} catch (r) {
reject(r);
}
});
self.onRejectedCallback.push(function (reason) {
try {
var x = onRejected(reason);
resolvePromise(promise2, x, resolve, reject);
} catch (r) {
reject(r);
}
});
}));
}
};
Promise.prototype.catch = function (onRejected) {
return this.then(null, onRejected);
};
// 最后这个是测试用的,后面会说
Promise.deferred = Promise.defer = function () {
var dfd = {};
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};
最终版的 Promise 的实现还是需要经过规范的测试(Promise /A+ 规范测试的工具地址为:https://github.com/promises-aplus/promises-tests (opens new window)),需要暴露一个 deferred 方法(即 exports.deferred 方法),上面提供的代码中我已经将其加了进去
npm i -g promises-aplus-tests
promises-aplus-tests Promise.js
# 模拟异步任务
queueMicrotask
https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_DOM_API/Microtask_guide (opens new window)
setTimeout
因为这个是宏任务,所以不适合模拟