Promise 是异步编程的一种解决方案,比传统的回调函数或事件更合理和更灵活。本文主要展示 Promise 提供的方法列表,以及基本的实现原理。通过本文,我们能加深 Promise 方法的理解和场景使用,对 Promise.all、Promise.race、Promise.allSettled 和 Promise.any 四个方法的异同之处也有更深层次的领悟。
一、Promise 方法列表
Promise 的实例方法有 then/catch/finally 三种,静态方法有 all/race/allSettled/any/resolve/reject 六种。其中 then 实现起来因为涉及 Promise 裁决过程(The Promise Resolution Procedure),会比较复杂,其余的都是基于已有功能的拓展。下面都给大伙列出所有的方法。
Promise.prototype.then()
Promise.prototype.catch()
Promise.prototype.finally()
Promise.all()
Promise.race)
Promise.allSettled)
Promise.any()
Promise.resolve()
Promise.reject()
二、Promise 九种方法实现
1. 原型方法 then
then 方法是整个 Promise 解决的核心内容,同时因为回调函数和返回一个新的 Promise 实例,因此决议过程比较复杂。
// class MyPromise {
// static PENDING = 'pending'; // 进行中
// static FULFILLED = 'fulfilled'; // 已成功
// static REJECTED = 'rejected'; // 已失败
// state = MyPromise.PENDING;
// value = null;
// reason = null;
// onFulfilledCallbacks = [];
// onRejectedCallbacks = [];
// ...
// }
MyPromise.prototype.then = (onFulfilled, onRejected) => {
if (typeof onFulfilled != 'function') {
onFulfilled = (value) => value;
}
if (typeof onRejected != 'function') {
onRejected = (reason) => {
throw reason;
};
}
// Promise 核心解决过程 见规范2.3
const _resolvePromise = (promise, x, resolve, reject) => {
// 2.3.1 如果 promise 和 x 指向同一对象,抛出 TypeError 错误
if (promise === x) {
const errMsg = 'The promise and the return value are the same';
return reject(new TypeError(errMsg));
}
// 2.3.3 如果 x 为对象(不是null)或者函数
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
let then = null;
try {
// 2.3.3.1. 检索属性 x.then
then = x.then;
} catch (error) {
// 2.3.3.2 如果 x.then 导致抛出异常 e,则以 e 为拒绝原因拒绝 promis
return reject(error);
}
// 2.3.3.3 如果 then 是一个函数,x 作为 then 的 this 调用该方法,第一个参数是成功的回调函数,第二个参数是失败的回调函数
if (typeof then === 'function') {
let called = false;
try {
then.call(
x,
(y) => {
// 2.3.3.3.4 如果成功回调与失败回调都被调用或多次调用同一个参数,则第一个调用优先,其他调用都将被忽略。
if (called) return;
called = true;
// 2.3.3.3.1 如果成功回调以值 y 调用,运行 [[Resolve]](promise,y)
_resolvePromise(promise, y, resolve, reject);
},
(r) => {
// 2.3.3.3.4 如果成功回调与失败回调都被调用或多次调用同一个参数,则第一个调用优先,其他调用都将被忽略。
if (called) return;
called = true;
// 2.3.3.3.2 如果失败回调以原因 r 调用,用 r 拒绝 promise
reject(r);
}
);
} catch (error) {
// 2.3.3.4 如果调用 then 方法抛出异常 e:
// 2.3.3.4.1 若成功回调或失败回调都调用过,忽略
if (called) return;
// 2.3.3.4.2 未调用,用 e 作为原因拒绝 promise
reject(error);
}
} else {
// 2.3.3.4. 如果 then 不是函数,用 x 作为值完成 promise
return resolve(x);
}
} else {
// 2.3.4 如果 x 不为对象或者函数,以 x 为参数执行 promise
return resolve(x);
}
};
// 链式返回的Promise
const newPromise = new MyPromise((resolve, reject) => {
switch (this.state) {
case MyPromise.FULFILLED:
setTimeout(() => {
try {
const x = onFulfilled(this.value);
_resolvePromise(newPromise, x, resolve, reject);
} catch (reason) {
reject(reason);
}
}, 0);
break;
case MyPromise.REJECTED:
setTimeout(() => {
try {
const x = onRejected(this.reason);
_resolvePromise(newPromise, x, resolve, reject);
} catch (reason) {
reject(reason);
}
}, 0);
break;
case MyPromise.PENDING:
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
_resolvePromise(newPromise, x, resolve, reject);
} catch (reason) {
reject(reason);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
_resolvePromise(newPromise, x, resolve, reject);
} catch (reason) {
reject(reason);
}
}, 0);
});
break;
}
});
return newPromise;
};
2. 原型方法 catch
如果上面没有定义 reject 方法或者在抛出错误,那么所有的异常会走向 catch 方法,而 catch 可以复用 then 方法。
MyPromise.prototype.catch = function (onRejected) {
return this.then(null, onRejected);
};
3. 原型方法 finally
不管是 resolve 还是 reject 都会调用 finally 。那么相当于 fianlly 方法替使用者分别调用了一次 then 的 resolved 和 rejected 状态回调。
MyPromise.prototype.finally = function (fn) {
return this.then(
(value) => {
fn();
return value;
},
(reason) => {
fn();
throw reason;
}
);
};
4. 静态方法 Promise.all
Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。所有参数数组 Promise 实例执行 resolve 回调后,新实例执行 resolve 回调;如果中间有任何一个 Promise 实例执行 reject 回调,那么新实例就直接执行 reject 回调了。
打个比方:多名员工分别同时进行多个项目,你要求任何一个项目都必须是令你满意的,有一个不满意都算这件事(所有项目)是失败的。强调的是整体令你满意。
MyPromise.all = function (promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
resolve([]);
} else {
let result = [];
let index = 0;
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(data) => {
result[i] = data;
if (++index === promises.length) {
resolve(result);
}
},
(err) => {
reject(err);
return;
}
);
}
}
});
};
5. 静态方法 Promise.race
Promise.race() 顾名思义,就是竞赛,返回最快完成那一个 Promise 实例。只要参数数组中有一个 Promise 实例执行 resolve 回调或 reject 回调后,新实例就直接返回结果。
打个比方:多名员工分别同时进行多个项目,你只要最快完成的项目,无论项目是否令你满意。强调的是要快。
MyPromise.race = function (promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
resolve();
} else {
let index = 0;
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(data) => {
resolve(data);
},
(err) => {
reject(err);
return;
}
);
}
}
});
};
6. 静态方法 Promise.allSettled
Promise.allSettled() 方法只有等到参数数组的所有 Promise 实例都发生状态变更,返回的 Promise 实例才会发生状态变更,无论是执行 resolve 回调还是 reject 回调的状态。
打个比方:多名员工分别同时进行多个项目,你要求每个一个项目都必须完成,然后得到所有项目是令你满意还是令你不满意的。强调的是最终结果。
MyPromise.allSettled = function (promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
resolve([]);
} else {
let result = [];
let index = 0;
for (let i = 0; i < promises.length; i++) {
promises[i]
.then(
(value) => {
result[i] = {
status: 'fulfilled',
value,
};
},
(reason) => {
result[i] = {
status: 'rejected',
reason,
};
}
)
.finally(() => {
if (++index === promises.length) {
return resolve(result);
}
});
}
}
});
};
同时因为 Promise.allSettled() 和 Promise.all() 都是对所有 Promise 实例的一种处理,下面就可以利用 Promise.all() 来实现 Promise.allSettled() 方法。
MyPromise.allSettled = function (promises) {
return Promise.all(
promises.map((item) =>
Promise.resolve(item).then(
(value) => ({ status: 'fulfilled', value }),
(reason) => ({ status: 'rejected', reason })
)
)
);
};
7. 静态方法 Promise.any
Promise.any() 方法是返回任意一个最快执行 resolve 回调的 Promise 实例。
打个比方:多名员工同时进行多个项目,你只要最快得到令你满意的那个项目,无论所有项目最终都没令你满意,这件事(所有项目)才算是失败了。强调的是又快又好。
MyPromise.any = function (promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
return resolve();
} else {
let result = [];
let index = 0;
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(value) => {
return resolve(value);
},
(reason) => {
result[i] = reason;
if (++index === promises.length) {
return reject(new AggregateError(result));
}
}
);
}
}
});
};
8. 静态方法 Promise.resolve
Promise.resolve 方法返回一个以给定值解析后的 Promise 实例。相当于执行 then 方法里面的 _resolvePromise。
MyPromise.reject = function (value) {
return new MyPromise((resolve, reject) => {
resolve(value);
});
};
9. 静态方法 Promise.reject
Promise.reject 方法返回一个带有拒绝原因的 Promise 实例。
MyPromise.reject = function (reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
};
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。