原稿:语雀 · Promise · 原目录:手撕代码 › Promise
参考链接:
Promise 实现
类构造器
- promise 有三类状态、解决值、拒绝理由,具有私有实例方法 resolve、reject
- 当 promise 处于 pending 态时,由 then() 添加的 onResolved/onRejected 回调需要在队列中暂存,等到期约状态落定时再去执行,因此需要两个保存回调的数组
- promise 构造时传入的 executor 会立刻去执行
基于这些基本要求,可以写出下方 promise 的类构造器
const PENDING = "PENDING";
const RESOLVED = "RESOLVED";
const REJECTED = "REJECTED";
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if (this.status === PENDING) {
this.status = RESOLVED;
this.value = value;
this.onResolvedCallbacks.forEach((fn) => fn());
}
};
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
}
then() & resolvePromise()
then 是 promise 类上的静态方法,接收两个回调 onResolved/onRejected,会在 promise 落定为 resolved 或 rejected 时执行,当 promise 未落定时,回调会先放入队列中
- 返回 promise:标准要求回调返回一个 promise,即 promise2,它包装自 onResolved/onRejected 的执行结果
- 当 onResolved/onRejected 的执行结果 x 是一个 promise(thenable) ,递归地调用 x.then(),直到取得最后的值后再包装为 promise2 的 value
- 当 x 是一个普通值,直接包装为 promise2 的 value
- 当中间抛出任何错误,直接包装为 promise2 的 reason
- 链式调用:then() 可以进行链式调用,前一个 then() 返回的 promise value/reason 会成为下一个 then() 中回调接收到的参数
- 值穿透:当未显式提供回调时,会直接传递上一个 promise value 或 reason
基于此可以写出 then() 与 resolvePromise(),其中在 resolvePromise 中自始至终传递的始终都是 promise2 的 resolve 与 reject,
// 包装 onResolved/onRjected 返回值 x
// x是普通值:直接返回
// x是thenable对象:递归的调用它的then()
const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x) {
return reject(new TypeError("Chaining cycle detected for promise!"));
}
let called;
// 当x是个thenable对象
if ((typeof x === "object" && x != null) || typeof x === "function") {
try {
let then = x.then;
if (typeof then === "function") {
then.call(
x,
(y) => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
},
(r) => {
if (called) return;
called = true;
reject(r);
}
);
// x是个普通值
} else {
resolve(x);
}
} catch (error) {
if (called) return;
called = true;
reject(error);
}
// x是个普通值
} else {
resolve(x);
}
};
const PENDING = "PENDING";
const RESOLVED = "RESOLVED";
const REJECTED = "REJECTED";
class Promise {
constructor(executor) { /* */ }
// 静态方法
then(onResolved, onRjected) {
// 值穿透
onResolved = typeof onResolved === "function" ? onResolved : (v) => v;
onRejected =
typeof onRjected === "function"
? onRjected
: (error) => {
throw error;
};
let promise2 = new Promise((resolve, reject) => {
// 箭头函数this在定义时确定,因此这里this即调用then()的promise1
if (this.status === RESOLVED) {
setTimeout(() => {
try {
let x = onResolved(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
Promise.all()
参考链接:promise详解(promise.all实现、promise.race实现)
说明 | Promise.all() 接收一个可迭代对象,返回一个新期约:该期约会在一组期约全部解决之后再解决
|
返回值 | 返回一个新期约
|
实现
Promise.all = (promises) => { return new Promise((resolve, reject) => { const result = []; let cnt = 0; for (let i = 0; i < promises.length; i++) { // 包装 Promise.resolve(promises[i]).then((value) => { cnt++; result[i] = value; if (cnt == promises.length) resolve(result); }, reject); } });};// 测试var p1 = Promise.resolve(3);var p2 = 1337;var p3 = new Promise((resolve, reject) => { setTimeout(resolve, 100, "foo");});Promise.all([p1, p2, p3]).then((values) => { console.log(values); // [3, 1337, "foo"]});
Promise.race()
说明 | Promise.race() 接收一个可迭代对象,返回一个包装期约,是一组集合中最先解决/拒绝的期约的镜像
|
返回值 | 返回一个新期约
|
实现
Promise.race = (promises) => {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(resolve, reject);
}
});
};
promise 限制并发数
class Scheduler {
constructor(max) {
this._max = max;
this._count = 0;
this._taskQueue = [];
}
add(caller, ...args) {
return new Promise((resolve, reject) => {
let task = this.createTask(caller, args, resolve, reject);
if (this._count >= this._max) this._taskQueue.push(task);
else task();
});
}
createTask(caller, args, resolve, reject) {
return () => {
caller(...args)
.then(resolve, reject)
.finally(() => {
this._count--;
if (this._taskQueue.length) {
let task = this._taskQueue.shift();
task();
}
});
this._count++;
};
}
}
// sleep()
const timeout = (time) =>
new Promise((resolve) => {
setTimeout(resolve, time);
});
const scheduler = new Scheduler(2);
// const addTask = (time, order) => {
// scheduler.add(() => timeout(time))
// .then(() => console.log(order));
// };
async function addTask(time, order) {
await scheduler.add(() => timeout(time));
console.log(order);
}
addTask(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");
// 2 3 1 4