基于ES6新特性——class关键字,我手写了一个简单的promise对象,目前已实现构造器函数,then方法,catch方法,resolve、reject、all、race静态方法
1、Promise简介
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise
对象。
所谓Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise
是一个对象,从它可以获取异步操作的消息。Promise
提供统一的 API,各种异步操作都可以用同样的方法进行处理。
2、MyPromise的总体结构
我手写的MyPromise
采用class
关键字声明类,定义了PENDING
、RESOLVED
、REJECTED
三个常量,用于表示MyPromise
实例的三种状态,类内定义了构造器函数、实例方法和静态方法,最后通过CommonJS
模块化规范向外暴露。
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
class MyPromise {
static resolve = (data) => {};
static reject = (data) => {};
static all = (promises) => {};
static race = (promises) => {};
constructor(excutor) {
//执行器函数
this.excutor = excutor;
//状态
this.status = PENDING;
//数据
this.data = undefined;
//回调队列
this.callbacks = [];
}
then = (onResolved, onRejected) => {};
catch = (onRejected) => {};
}
module.exports = MyPromise;
3、MyPromise的构造器
构造器constructor
是MyPromise的核心部分,其中声明了执行器函数、状态、值和回调队列四个属性。
constructor(excutor) {
//执行器函数
this.excutor = excutor;
//状态
this.status = PENDING;
//值
this.data = undefined;
//回调队列
this.callbacks = [];
this.resolve = (data) => {
if (this.status !== PENDING) {
//如果不是挂起状态, 则直接返回
return;
} else {
this.status = RESOLVED;
this.data = data;
//执行回调队列中的回调函数
if (this.callbacks.length > 0) {
this.callbacks.forEach((callbackObj) => {
setTimeout(() => {
callbackObj.onResolved();
});
});
}
}
};
this.reject = (data) => {
if (this.status !== PENDING) {
//如果不是挂起状态, 则直接返回
return;
} else {
this.status = REJECTED;
this.data = data;
if (this.callbacks.length > 0) {
this.callbacks.forEach((callbackObj) => {
setTimeout(() => {
callbackObj.onRejected();
});
});
}
}
};
try {
excutor(this.resolve, this.reject);
} catch (error) {
reject(error);
}
}
执行器函数excutor
内部有两个参数, 用于确定MyPromise
实例的状态和内部的值,我声明为this.resolve
和this.reject
。以this.resolve
为例,调用时先判断此时MyPromise
实例的状态,如果不是pending
,则直接返回;否则赋予MyPromise
成功的状态resolved
与相应的this.data
,在之后并判断回调队列中是否有待执行的任务,如果有,则遍历并全部执行。关于回调队列会在then
方法中解释。
4、实例方法then
MyPromise
实例具有then
方法,它的作用是为 MyPromise
实例添加状态改变时的回调函数。前面说过,then
方法的第一个参数是resolved
状态的回调函数,第二个参数是rejected
状态的回调函数,它们都是可选的。
then
方法内部稍微有些复杂,我将分块进行介绍。
then = (onResolved, onRejected) => {
onResolved =
typeof onResolved === "function" ? onResolved : (value) => value;
onRejected =
typeof onRejected === "function"
? onRejected
: (error) => {
throw error;
};
return new MyPromise((resolve, reject) => {
const handle = (callback) => {
setTimeout(() => {
try {
const result = callback(this.data);
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
});
};
if (this.status === RESOLVED) {
handle(onResolved);
} else if (this.status === REJECTED) {
handle(onRejected);
} else {
//如果promise仍属于挂起状态, 则加入回调队列
this.callbacks.push({
onResolved() {
handle(onResolved);
},
onRejected() {
handle(onRejected);
},
});
}
});
};
onResolved =
typeof onResolved === "`function`" ? onResolved : (value) => value;
onRejected =
typeof onRejected === "function"
? onRejected
: (error) => { throw error; };
onResolved
和onRejected
是then
方法的参数,分别表示成功和失败的回调,此处对onResolved
和onRejected
进行类型判断,主要是为了实现MyPromise
的穿透功能,如果使用者已经在then
方法中传入函数(即typeof onResolved/ onRejected=== "function"
),则不进行任何处理,否则赋予(value) => value
或(error) => { throw error; }
,将状态与值向后传递,在之后调用then
方法进行处理。
return new MyPromise((resolve, reject) => {});
我们知道ES6新特性中Promise的then
方法会返回一个新的Promise实例,因此我们通过return new MyPromise((resolve, reject) => {});
包裹内部的核心代码,保证then方法能够返回一个MyPromise
实例。
if (this.status === RESOLVED) {
handle(onResolved);
} else if (this.status === REJECTED) {
handle(onRejected);
} else {
//如果promise仍属于挂起状态, 则加入回调队列
this.callbacks.push({
onResolved() {
handle(onResolved);
},
onRejected() {
handle(onRejected);
},
});
}
这里对this.status
进行判断,不同的状态执行不同的代码块, handle是对于成功和失败状态共同代码部分的封装,稍微进行分析; 当MyPromise
实例处于挂起状态时,则加入回调队列,等待MyPromise
实例状态改变后再执行。
const handle = (callback) => {
setTimeout(() => {
try {
const result = callback(this.data);
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
});
handle
函数是对成功onResolved
和失败onRejected
回调中相同代码的封装,它的作用主要是执行onResolved
或onRejected
回调,并返回一个新的MyPromise
实例,以实现其链式调用功能,它通过setTimeout
模拟异步效果。result instanceof MyPromise
是对onResolved
或onRejected
回调返回的结果进行判断,如果是MyPromise
,它就是回调的返回结果,否则调用resolve(result)
,将结果封装到MyPromise
的内部返回。为了保证代码的严谨性,我在最外部还用try catch
进行包裹,如果有错误代码,则捕获并返回一个失败的MyPromise
实例
5、catch方法
catch = (onRejected) => {
this.then(()=>{}, onRejected);
}
catch
实际上就是个语法糖myPromise.catch(onRejected)
就相当于 myPromise.then(() => {}, onRejected)
6、resolve静态方法和reject静态方法
static resolve = (data) => {
return new MyPromise((resolve, reject) => {
resolve(data);
})
}
static reject = (data) => {
return new MyPromise((resolve, reject) => {
reject(data);
})
}
MyPromise.resolve(data)
和MyPromise.reject(data)
,同样是语法糖,MyPromise.resolve(data)
就相当于new MyPromise((resolve, reject) => {resolve(data);})
对于ES6新特性中Promise API了解的朋友应该很容易写出
7、all静态方法
Promise.all()
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
p
的状态由p1
、p2
、p3
决定,分成两种情况。
(1)只有p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。
(2)只要p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
static all = (promises) => {
let resolvedCount = 0;
let values = new Array(promises.length);
return new MyPromise((resolve, reject) => {
//遍历获取每个promise的结果
promises.forEach((p, index) => {
p.then(
(value) => {
values[index] = value;
resolvedCount++;
if (resolvedCount === promises.length) {
resolve(values);
}
},
(reason) => {
reject(reason);
}
);
});
});
};
在我的代码中,定义values
变量用于保存成功的值,变量resolvedCount
用于对成功的值进行计数,之后遍历数组promises
,如果失败, 直接通过reject(reason)
使all
方法返回一个失败的MyPromise
实例, 如果成功,则将值存到values数组中相应的位置中(注意:这里成功的值在values
数组中的位置取决于promises
数组中对应实例的位置,而不是完成的先后顺序),然后使resolvedCount
加1,判断resolvedCount
的值,如果与promises
长度相等,则说明所有的promises
数组中所有的MyPromises
实例都已经返回了成功的值。
8、race静态方法
Promise.race()
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数。
static race = (promises) => {
return new MyPromise((resolve, reject) => {
promises.forEach((p) => {
p.then(
(value) => {
resolve(value);
},
(reason) => {
reject(reason);
}
);
});
});
};
race
的实现与all
类似,都是对promises
数组进行遍历取值。