手写Promise实现
2020-05-02 本文已影响0人
风雅欢乐
初学ES 6的Promise之后, 感觉这个新的异步处理模型有点绕, 为了加深理解, 所以尝试手写Promise的实现, 尽可能的实现原生的Promise的方法和效果.
在写代码之前, 先对Promise相关的知识和使用方式进行整理.
Promise的状态
Promise作为ES 6标准中提出的异步处理模型, 它分为两个阶段, 三种状态.
- 未决阶段 -- 对应状态为pending
- 已决阶段 -- 对应状态为resolved或rejected
Promise只能从未决阶段, 发展到已决阶段, 并且状态一旦发生改变, 就固定下来不能再变化.
Promise的使用
Promise是一个构造函数, 它有一个参数, 这个参数是一个函数, 表示在未决阶段需要执行的代码. 这个函数提供了两个参数resolve和reject, 这两个参数也是函数, 分别表示将未决阶段推向已决阶段的resolved状态或rejected状态. 通常, 在Promise的参数中, 执行异步操作如ajax请求, 定时器等异步代码.
Promise有两个实例方法
- then: 表示注册已决阶段的处理函数, 可以有两个参数
- 第一个参数是一个函数, 表示注册一个已决阶段resolved状态的处理函数, 该函数接收的参数data表示在之前未决阶段执行代码中, resolve函数传入的数据参数
- 第二个参数可选, 也是一个函数, 表示注册一个已决阶段rejected状态的处理函数, 该函数接收的参数error表示之前未决阶段执行代码中, reject函数传入的错误信息
- catch: 表示注册已决阶段rejected状态的处理函数, 效果同then方法的第二个参数
then和catch方法都返回一个新的Promise
Promise有几个静态方法
- resolve: 直接返回一个resolved状态的Promise
- reject: 直接返回一个rejected状态的Promise
- race: 返回一个Promise, 参数为多个Promise组成的数组, 只要有一个变为resolved状态, 它就变为resolved状态, 当有一个变为rejected时, 它就rejected
- all: 返回一个Promise, 参数为多个Promise组成的数组, 只有当全部Promise都变成resolved状态时, 它才变为resolved, 当有一个变为rejected时, 它就rejected
代码如下
const MyPromise = (function (undefined) {
const PENDING = 'pending',
RESOLVED = 'resolved',
REJECTED = 'rejected';
const PromiseStatus = Symbol('PromiseStatus'); // 内部变量, 表示promise当前的状态
const PromiseValue = Symbol('PromiseValue'); // 内部变量, 表示promise的值
const changeStatus = Symbol('changeStatus'); // 内部函数, 改变promise的状态
const thenables = Symbol('thenables'); // 所有的resolved状态的处理函数的队列
const catchables = Symbol('catchables'); // 所有的rejected状态的处理函数的队列
const settleHandler = Symbol('settleHandler'); // 内部函数, 注册处理函数
const linkPromise = Symbol('linkPromise'); // 内部函数, 返回新的Promise
return class MyPromise {
constructor(executor) {
this[PromiseStatus] = PENDING;
this[PromiseValue] = undefined;
this[catchables] = [];
this[thenables] = [];
// 使用箭头函数的形式来定义resolve和reject函数是为了保持this的指向
const resolve = (data) => {
this[changeStatus](RESOLVED, data, this[thenables]);
}
const reject = (error) => {
this[changeStatus](REJECTED, error, this[catchables]);
}
// 在未决阶段代码执行的过程中, 如果发生错误, promise直接进入rejected状态, 所以要对错误进行捕获
try {
executor(resolve, reject);
} catch(err) {
reject(err);
}
}
/**
* 改变状态
* @param {*} newStatus 改变到的目标状态
* @param {*} newValue promise的值
* @param {*} 状态改变后, 需要执行哪个状态的队列
*/
[changeStatus](newStatus, newValue, queue) {
// 如果promise不是在pending状态了, 那么resolve和reject都不会有任何效果
if (this[PromiseStatus] !== PENDING) {
return;
}
this[PromiseStatus] = newStatus;
this[PromiseValue] = newValue;
queue.forEach(handler => handler(this[PromiseValue]));
}
/**
* 内部函数
* 由于使用then和catch方法时会返回一个新的promise, 如果原先的promise已经是resolved状态, 则新promise也变成resolved状态
* 那么新的promise怎么知道旧的promise什么时候resolve呢?
* 答案是如果旧promise注册的then函数执行了, 那么说明它resolve了, 那么新的promise也可以resolve
* 所以要对旧promise的thenable和catchable函数再封装一层函数, 假设称呼为F, 实际上把这个F添加到旧的promise的队列中
* 那么只要旧promise状态变化执行了这个F, 也就执行了F里的触发新promise状态改变的代码
*/
[linkPromise](thenable, catchable) {
return new MyPromise((resolve, reject) => {
// 将thenable和catchable添加注册
// 这里第一个参数就是经过再一层包装的函数
this[settleHandler]((data) => {
// 如果thenable不是一个函数, 说明它没注册处理程序, 那么就直接将旧promise返回的data直接resolve, 由新promise的处理程序去处理
if (typeof thenable !== 'function') {
resolve(data);
return;
}
exec(thenable, data, resolve, reject);
}, RESOLVED, this[thenables]);
this[settleHandler]((error) => {
if (typeof catchable !== 'function') {
reject(error);
return;
}
exec(catchable, error, resolve, reject);
}, REJECTED, this[catchables]);
});
// 公共代码抽取函数: 新的promise resolve之前注册的处理函数的处理结果
function exec(handler, data, resolve, reject) {
try {
const result = handler(data);
// 如果之前的处理函数返回的又是一个promise, 称为P. 则新promise当P resolve的时候resolve, 当P reject的时候reject
if (result instanceof MyPromise) {
result.then( d => {
resolve(d);
}, e => {
reject(e);
});
} else {
// 否则, 直接resolve 这个result结果
resolve(result);
}
} catch (err) {
reject(err);
}
}
}
/**
* 内部函数: 如果状态已经处于已决阶段, 则直接执行处理函数, 否则添加到响应的队列中
* @param {*} handler 处理函数
* @param {*} immediateStatus 相应的状态
* @param {*} queue 需要添加到的队列
*/
[settleHandler](handler, immediateStatus, queue) {
// 如果handler不是函数, 那么不做任何事情
if (typeof handler !== 'function') {
return;
}
// 如果当前的状态不是pending, 而是resolved或者rejected, 那么立即运行处理函数
if (this[PromiseStatus] === immediateStatus) {
// 注册的函数是异步执行的, 所以此处使用setTimeout模拟异步
// 区别是promise的异步处理函数是加入微队列中, 而setTimeout的处理函数是加入到宏队列中
setTimeout(() => {
handler(this[PromiseValue]);
}, 0);
} else {
queue.push(handler);
}
}
then(thenable, catchable) {
return this[linkPromise](thenable, catchable);
}
catch(catchable) {
return this[linkPromise](undefined, catchable);
}
finally(callback) {
return this.then(val => {
MyPromise.resolve(callback()).then(() => val);
}, err => {
MyPromise.resolve(callback()).catch(() => { throw err });
});
}
/**
* 静态方法
* 如果参数是一个promise, 则返回promise本身, 否则返回一个新Promise, 并且直接resolve data数据
* @param {*} 需要直接resolve的内容
*/
static resolve(data) {
if (data instanceof MyPromise) {
return data;
} else {
return new MyPromise(resolve => {
resolve(data);
});
}
}
static reject(error) {
return new MyPromise((resolve, reject) => {
reject(error);
});
}
static all(proms) {
return new MyPromise((resolve, reject) => {
const results = proms.map(p => {
const obj = {
result: undefined,
isResolved: false,
};
p.then(data => {
obj.isResolved = true;
obj.result = data;
// 判断如果结果中没有未resolve的了, 那么才可以resolve
const unResolved = results.find(r => !r.isResolved);
if (!unResolved) {
// resolve的内容为所有promise的数据
resolve(results.map(rst => rst.result));
}
}, err => {
// 如果有任何一个promise reject了, 那么新promise也reject
reject(err);
});
return obj;
});
});
}
static race(proms) {
return new MyPromise((resolve, reject) => {
proms.forEach(p => {
p.then(data => {
resolve(data);
}, err => {
reject(err);
});
});
});
}
}
})(void 0);