Promise
的声明
当我们使用Promise
的时候,通常都是new Promise((resolve, reject) => {})
。
因此我们可以看出:
-
Promise
是一个类; -
Promise
类的构造函数的第一个参数是函数,这个函数叫处理器函数(executor function
); - 而在处理器函数中,有了两个参数:
resolve
和reject
- 当异步任务顺利完成且返回结果值的时候,我们会调用
resolve
函数; - 当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用
reject
函数。
- 当异步任务顺利完成且返回结果值的时候,我们会调用
因此,我们可以初步声明一下Promise
类。
class Promise {
/**
* 构造器
* @returns {Promise<object>}
* @param executor<function>: executor有两个参数:resolve和reject
*/
constructor(executor) {
// resolve 成功
const resolve = () => {};
// reject 失败
const reject = () => {};
// 执行 executor
executor(resolve,reject);
}
}
实现Promise的基本状态
Promise
存在着三种状态:pending
(等待态)、fulfilled
(成功态)和rejected
(失败态):
-
Promise
的初始状态是pending
状态; -
pending
状态可以转换为fulfilled
状态和rejected
状态; -
fulfilled
状态不可以转为其他状态,且必须有一个不可改变的值(value); -
rejected
状态不可以转为其他状态,且必须有一个不可改变的原因(reason); - 当在处理器函数中调用
resolve
函数并传入参数value,则状态改变为fulfilled
,且不可以改变; - 当在处理器函数中调用
reject
函数并传入参数reason,则状态改变为rejected
,且不可以改变; - 若处理器函数执行中报错,直接执行
reject
函数。
因此,我们需要在Promise
类中设置三个变量:state
(状态变量),value
(成功值的变量)和reason
(失败原因的变量),然后在resolve
函数、reject
函数以及执行executor
函数报错的时候改变state
的值。
class Promise {
constructor(executor) {
// 初始化状态
this.state = 'pending';
// 成功的值
this.value = undefined;
// 失败的原因
this.reason = undefined;
/**
* resolve 成功函数
* @param value<any>: 成功的值
*/
const resolve = (value) => {
// 只能在状态为pending的时候执行
if(this.state === 'pending'){
// resolve调用后,state转化为fulfilled
this.state = 'fulfilled';
// 存储value
this.value = value;
}
};
/**
* reject 失败函数
* @param reason<any>: 失败的原因
*/
const reject = (reason) => {
// 只能在状态为pending的时候执行
if(this.state === 'pending'){
// resolve调用后,state转化为rejected
this.state = 'rejected';
// 存储reason
this.reason = reason;
}
};
// 如果executor执行报错,直接执行reject()
try {
executor(resolve,reject);
}catch (e){
reject(e);
}
}
}
then
方法
Promise
有一个then
方法,而该方法中有两个参数:onFulfilled
和onRejected
:
- 这两个参数都是一个函数,且会返回一个结果值;
- 当状态为
fulfilled
,只执行onFulfilled
,传入this.value
; - 当状态为
rejected
,只执行onRejected
,传入this.reason
;
因此我们可以来实现一下then
方法。
class Promise {
constructor(executor) {...}
/**
* then 方法
* @param onFulfilled<function>: 状态为fulfilled时调用
* @param onRejected<function>: 状态为rejected时调用
*/
then(onFulfilled, onRejected) {
// 状态为fulfilled的时候,执行onFulfilled,并传入this.value
if(this.state === 'fulfilled'){
/**
* onFulfilled 方法
* @param value<function>: 成功的结果
*/
onFulfilled(this.value)
}
// 状态为rejected的时候,onRejected,并传入this.reason
if(this.state === 'rejected'){
/**
* onRejected 方法
* @param reason<function>: 失败的原因
*/
onRejected(this.reason)
}
}
}
异步实现
Promise
实际上一个异步操作:
-
resolve()
是在setTimeout
内执行的; - 当执行
then()
函数时,如果状态是pending
时,我们需要等待状态结束后,才继续执行,因此此时我们需要将then()
的两个参数onFulfilled
和onRejected
存起来; - 因为一个
Promise
实例可以调用多次then()
,因此我们需要将onFulfilled
和onRejected
各种用数组存起来。
因此我们可以借着完善代码:
class Promise {
/**
* 构造器
* @returns {Promise<object>}
* @param executor<function>: executor有两个参数:resolve和reject
*/
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
// 存储onFulfilled的数组
this.onResolvedCallbacks = [];
// 存储onRejected的数组
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
// 一旦resolve执行,调用onResolvedCallbacks数组的函数
this.onResolvedCallbacks.forEach(fn => fn());
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
// 一旦reject执行,调用onRejectedCallbacks数组的函数
this.onRejectedCallbacks.forEach(fn=>fn());
}
};
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
onFulfilled(this.value)
}
if (this.state === 'rejected') {
onRejected(this.reason)
}
// 状态为pending的时候,将onFulfilled、onRejected存入数组
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
onFulfilled(this.value)
})
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
}
}
}
实现链式调用
我们常常会像下面代码一样使用Promise
:
new Promise()
.then()
.then()
.then()
这种方法叫做链式调用,通常是用来解决回调地狱(Callback Hell
)的,就如下的代码:
fs.readdir(source, function (err, files) {
if (err) {
console.log('Error finding files: ' + err)
} else {
files.forEach(function (filename, fileIndex) {
console.log(filename)
gm(source + filename).size(function (err, values) {
if (err) {
console.log('Error identifying file size: ' + err)
} else {
console.log(filename + ' : ' + values)
aspect = (values.width / values.height)
widths.forEach(function (width, widthIndex) {
height = Math.round(width / aspect)
console.log('resizing ' + filename + 'to ' + height + 'x' + height)
this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
if (err) console.log('Error writing file: ' + err)
})
}.bind(this))
}
})
})
}
})
为了实现链式调用,我们需要满足一下几点:
- 我们需要在
then()
返回一个新的Promise
实例; - 如果上一个
then()
返回了一个值,则这个值就是onFulfilled()
或者onRejected()
的值,我们需要把这个值传递到下一个then()
中。
而对于上一个then()
的返回值,我们需要对齐进行一定的处理,因此封装一个resolvePromise()
的方法去进行判断处理;
接下来我们对then()
方法进行修改:
class Promise {
constructor(executor) { ... }
/**
* then 方法
* @returns {Promise<object>}
* @param onFulfilled<function>: 状态为fulfilled时调用
* @param onRejected<function>: 状态为rejected时调用
*/
then(onFulfilled, onRejected) {
// 返回一个新的Promise实例
const newPromise = new Promise((resolve, reject) => {
if (this.state === 'fulfilled') {
const x = onFulfilled(this.value)
// 对返回值进行处理
resolvePromise(newPromise, x, resolve, reject);
}
if (this.state === 'rejected') {
const x = onRejected(this.reason);
// 对返回值进行处理
resolvePromise(x, resolve, reject);
}
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
const x = onFulfilled(this.value);
// 对返回值进行处理
resolvePromise(newPromise, x, resolve, reject);
})
this.onRejectedCallbacks.push(() => {
const x = onRejected(this.reason);
// 对返回值进行处理
resolvePromise(newPromise, x, resolve, reject);
})
}
});
return newPromise;
}
}
function resolvePromise() {}
完成resolvePromise
函数
对于上一个then()
的返回值,我们用x
变量存起来,然后需要对它进行一个处理:
- 判断
x
是不是Promise
实例;- 如果是
Promise
实例,则取它的结果,作为新的Promise
实例成功的结果; - 如果是普通值,直接作为
Promise
成功的结果;
- 如果是
然后我们处理返回值后,需要利用newPromise
的resolve
和reject
方法将结果返回。
这里我们还需要注意一个地方,就是x
等于newPromise
的话,这时会造成循环引用,导致死循环。
let p = new Promise(resolve => {
resolve(0);
});
const p2 = p.then(data => {
// 循环引用,自己等待自己完成,导致死循环
return p2;
})
因此,resolvePromise
函数需要4个参数,即newPromise
,x
、resolve
和reject
。
所以我们来实现一下resolvePromise
函数:
/**
* resolvePromise 方法
* @param newPromise<object>: 新的Promise实例
* @param x<any>: 上一个then()的返回值
* @param resolve<function>:Promise实例的resolve方法
* @param reject<function>:Promise实例的reject方法
*/
function resolvePromise(newPromise, x, resolve, reject) {
// 循环引用报错
if(x === newPromise){
// reject报错
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 防止多次调用
let called;
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then;
// x 为Promise实例
if (typeof then === 'function') {
// 使用call执行then(),call的第一个参数是this,后续即then()的参数,即第二个是成功的回调方法,第三个为失败的回调函数
then.call(x, y => {
// 成功和失败只能调用一个
if(called)return;
called = true;
// resolve 的结果依旧是promise实例,那就继续解析
resolvePromise(newPromise, y, resolve, reject);
}, err => {
// 成功和失败只能调用一个
if(called)return;
called = true;
// 失败了就直接返回reject报错
reject(err);
})
} else {
// x 为普通的对象或方法,直接返回
resolve(x);
}
} catch (e) {
if(called)return;
called = true;
reject(e);
}
} else {
// x 为普通的值,直接返回
resolve(x);
}
}
onFulfilled
和onRejected
关于then()
的两个参数——onFulfilled
和onRejected
:
- 它们都是可选参数,而且它们都是函数,如果不是函数的话,就会被忽略掉;
- 如果
onFulfilled
不是一个函数,就将它直接替换成函数value => value
; - 如果
onRejected
不是一个函数,就将它直接替换成函数err => {throw err}
;
- 如果
class Promise {
constructor(executor) { ... }
then(onFulfilled, onRejected) {
// onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
// onRejected如果不是函数,就忽略onRejected,直接抛出错误
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
...
}
}
其次,onFulfilled
和onRejected
是不能同步被调用的,必须异步调用。因此我们就用setTimeout
解决一步问题。
class Promise {
constructor(executor) { ... }
then(onFulfilled, onRejected) {
// onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
// onRejected如果不是函数,就忽略onRejected,直接抛出错误
onRejected = typeof onRejected === 'function' ? onRejected : err => {
throw err
};
return new Promise((resolve, reject) => {
if (this.state === 'fulfilled') {
// 异步调用
setTimeout(() => {
try {
const x = onFulfilled(this.value)
resolvePromise(x, resolve, reject);
}catch (e){
reject(e)
}
})
}
if (this.state === 'rejected') {
// 异步调用
setTimeout(() => {
try{
const x = onRejected(this.reason);
resolvePromise(x, resolve, reject);
}catch (e){
reject(e)
}
})
}
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
// 异步调用
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(x, resolve, reject);
}catch (e){
reject(e)
}
})
})
this.onRejectedCallbacks.push(() => {
// 异步调用
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(x, resolve, reject);
}catch (e){
reject(e)
}
})
})
}
});
}
}
实现Promise
的其他方法
Promise.all()
Promise.all()
方法接收一个promise
的iterable
类型的输入,包括Array
、Map
、Set
。然后返回一个Promise
实例,该实例回调返回的结果是一个数组,包含输入所有promise
的回调结果。
但只要任何一个输入的promise
的reject
回调执行或者输入不合法的promise
,就会立马抛出错误。
/**
* Promise.all 方法
* @returns {Promise<object>}
* @param promises<iterable>: 一个promise的iterable类型输入
*/
Promise.all = function (promises) {
let arr = [];
return new Promise((resolve, reject) => {
if (!promises.length) resolve([]);
// 遍历promises
for(const promise of promises) {
promise.then(res => {
arr.push(res);
if(arr.length === promises.length){
resolve(arr);
}
}, reject)
}
})
}
Promise.allSettled()
Promise.allSettled()
其实跟Promise.all()
很像,同样是接收一个promise
的iterable
类型的输入,但返回的是一个给定的promise
已经完成后的promise
,并带有一个对象数组,每个对象标识着对应的promise
结果。
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).
then((results) => console.log(results));
// > Array [Object { status: "fulfilled", value: 3 }, Object { status: "rejected", reason: "foo" }]
实现:
/**
* Promise.allSettled 方法
* @returns {Promise<object>}
* @param promises<iterable>: 一个promise的iterable类型输入
*/
Promise.allSettled = function (promises) {
let arr = [];
return new Promise((resolve, reject) => {
try {
const processData = (data) => {
arr.push(data);
if(arr.length === promises.length){
resolve(arr);
}
}
if (!promises.length) resolve([]);
// 遍历promises
for(const promise of promises) {
promise.then(res => {
processData({state:'fulfilled', value: res})
}, err => {
processData({state:'rejected', reason: err})
})
}
}catch (e){
reject(e)
}
})
}
Promise.any()
Promise.any()
跟Promise.all()
和Promise.allSettled()一样,同样是接收一个
promise的
iterable类型的输入。但只要其中的一个
promise成功,就返回那个已经成功的
promise,但如果没有一个
promise成功,就返回一个失败的
promise`。
/**
* Promise.any 方法
* @returns {Promise<object>}
* @param promises<iterable>: 一个promise的iterable类型输入
*/
Promise.any = function (promises) {
return new Promise((resolve, reject) => {
// 如果传入的参数是一个空的可迭代对象,则返回一个 已失败(already rejected) 状态的 Promise
if (!promises.length) reject();
// 如果传入的参数不包含任何 promise,则返回一个 异步完成 (asynchronously resolved)的 Promise。
if (typeof promises[Symbol.iterator] !== 'function' ||
promises === null ||
typeof promises === 'string') {
resolve()
}
let i = 0;
// 遍历promises
for (const promise of promises) {
promise.then(res => {
i++;
resolve(res);
}, err => {
i++;
if (i === promises.length) {
reject(err);
}
})
}
})
}
Promise.race()
Promise.race()
,同样是接收一个promise
的iterable
类型的输入。一旦迭代器中的某个promise
完成了,不管是成功还是失败,就会返回这个promise
。
/**
* Promise.race 方法
* @returns {Promise<object>}
* @param promises<iterable>: 一个promise的iterable类型输入
*/
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (const promise of promises) {
promise.then(resolve, reject)
}
})
}
Promise.reject()
和Promise.resolve()
Promise.reject()
方法返回一个带有拒绝原因的Promise
对象;Promise.resolve()
方法返回一个以定值解析后的Promise
对象。
/**
* Promise.reject 方法
* @returns {Promise<object>}
* @param val<any>
*/
Promise.reject = function (val) {
return new Promise(reject => reject(val))
}
/**
* Promise.resolve 方法
* @returns {Promise<object>}
* @param val<any>
*/
Promise.resolve = function (val) {
return new Promise(resolve => resolve(val))
}
catch()
和finally()
catch()
方法是用来处理失败的情况,它传入一个处理函数,然后返回一个promise
实例。实际上它是then()
的语法糖,只接受rejected
态的数据。
finally()
是在promise
结束时,无论结果是fufilled
还是rejected
,都会执行指定的回调函数。同样也返回一个promise
实例。
class Promise {
constructor(executor) { ... }
then(onFulfilled, onRejected) { ... }
/**
* catch 方法
* @returns {Promise<object>}
* @param callback<function>: 处理函数
*/
catch(callback) {
return this.then(null, callback);
}
/**
* finally 方法
* @returns {Promise<object>}
* @param callback<function>: 处理函数
*/
finally(callback) {
return this.then(res => {
return Promise.resolve(callback()).then(() => res)
}, err => {
return Promise.reject(callback()).then(() => {
throw err
})
})
}
}
Promise/A+测试
Promise/A+
规范: https://github.com/promises-aplus/promises-spec
Promise/A+
测试工具: https://github.com/promises-aplus/promises-tests
安装promises-aplus-tests
插件。
yarn add promises-aplus-tests
在Promise.js
后面插入下列代码。
// 测试
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
module.exports = Promise;
然后输入命令行进行测试。
promises-aplus-tests Promise.js
结果:
872 passing (18s)