手撕Promise
/**
* 手写Promise源码
* 协商命名法则 _开头标识当前方法为内部方法,不提供给外部使用,无法做强制命名规范
*
* @format
*/
/**
* Promise使用方式
* const pro = new Promise((resolve,reject)=>{
* })
* pro.then(res=>res)
* pro.then(d=>d) then可以调用多次
* pro.catch(e=>e)
* Promise.all([p1,p2,p3]).then(res=>[p1res,p2res,p3res])
* Promise.resolve()
* 根据使用场景分析
* 1,Promise 构造函数参数为一个函数,并给函数传递两个函数参数
* 2,Promise 实例方法有,then、catch
* 3,Promise 静态方法 resolve、reject、all
* 4,Promise 实例是有状态的 pending fuilled reject
* */
/* 根据使用分析,并使用 ES6 class 构建自己的Promsie构造函数 */
/* 统一设置静态变量,内部使用变量对比 */
const PENDING = 'pending';
const RESOLVE = 'fuilled';
const REJECT = 'reject';
/**
* 判断是否符合PromiseA+ 规范
* @param pro promise
*/
function isPromise(pro) {
return !!(pro && typeof pro === 'object' && pro.then && typeof pro.then === 'function');
}
class $Promise {
/**
*
* @param {Function} executor
*/
constructor(executor) {
// 设置初始状态
this._state = PENDING;
// 设置初始值
this._value = null;
// 由于 then 是可以多次调用的,该值用于绑定 then 的回调函数
this._handles = [];
// 函数一旦执行报错了,自动变为失败的状态
try {
/**
* 为什么要使用 bind 返回一个 新的函数
* 因为在使用的时候是直接使用执行的
* 在执行上下文中,this指向了 window,严格模式下会指向 undefined
* */
executor(this._resolved.bind(this), this._reject.bind(this));
} catch (error) {
this._reject(error);
}
}
/**
*
* @param executor then中添加的函数
* @param state 该函数在什么状态下执行
* @param resolve 让 then 函数返回的 promise 返回成功
* @param reject 让 then 函数返回的 promise 返回失败
* resolve reject 执行后让then后面的then可以链式调用
*/
_pushHandler(executor, state, resolve, reject) {
this._handles.push({
executor,
state,
resolve,
reject,
});
}
/**
* 执行回调函数队列
*/
_runHandlers() {
// 只有状态改变了才能执行
if (this._state === PENDING) return;
while (this._handles[0]) {
this._handleRunTime(this._handles[0]);
// 执行完成后需要将当前的回调删除
/**
* 不删除会出现多次调用问题
* 场景
* Pro.then(function A(){})
* Pro.then(function B(){})
* 二次then的时候会执行一次的回调
*/
this._handles.shift();
}
}
/**
*
* @param handle 处理单个函数执行结果
*/
_handleRunTime({ executor, state, resolve, reject }) {
// 需要放至到微任务队列中,该方法就不做了,待后续更新
// 记录了多个回调函数,包括成功和失败的,如果成功了就不需要执行失败
if (this._state !== state) return;
// 根据规范,如果回调函数不是一个函数,需要将值透传的到下一个
// 一旦发生 reject 最近的catch 能捕获到,后续的 catch不能捕获到这次错误
// 后续的 catch 捕获的新的Promise
if (typeof executor !== 'function') {
return this._state === RESOLVE ? resolve(this._value) : reject(this._value);
}
// 一旦在回调函数中执行错误了,将新的Promise自动返回 失败
try {
const result = executor(this._value);
if (isPromise(result)) {
return result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
}
/**
* 优化代码减少冗余代码,提取公共代码
* 记录并修改状态和数据
* @param state 用于改变当前Promise的状态
* @param value 记录执行的结果,并传入then的回调函数中
*/
_changState(state, value) {
// 状态不允许多次改变
if (this._state !== PENDING) return;
this._state = state;
this._value = value;
// 状态发生改变执行 then catch 回调函数
this._runHandlers();
}
/**
*
* @param {any} data 任务完成时的数据
*/
_resolved(data) {
this._changState(RESOLVE, data);
}
/**
*
* @param reson 任务失败时的数据
*/
_reject(reson) {
this._changState(REJECT, reson);
}
/**
*
* @param onFuilled 任务成功时执行的函数
* @param onRejected 任务失败时执行的函数
*/
then(onFuilled, onRejected) {
// 根据规范 then 会返回一个新的Promise
// 做一个记录 外部的Promsie 叫 A 新返回的Promise 叫 B
const _that = this;
return new $Promise((resolve, reject) => {
/* 由于 then 中的函数
* 需要在 A 参数(executor)函数执行 _resolve,_reject 方法后执行
* 故先将 回调函数记录下来
*/
// 在这个里面使用 this 类似使用that
// 当前的this还是还是指向 _that
this._pushHandler(onFuilled, RESOLVE, resolve, reject);
this._pushHandler(onRejected, REJECT, resolve, reject);
// 这个promise的 resolve 什么时候执行呢
// 将执行函数加入到函数执行队列中
// 并查看当前状态是否完成并执行回调
this._runHandlers();
});
}
/**
*
* @param onRejected 任务失败时执行的函数
*/
catch(onRejected) {
// 根据规范 cath 会返回一个新的Promise
// 也符合 then 的逻辑,所以只给 then 传入失败的函数就可以了
return this.then(undefined, onRejected);
}
/**
* 简单写
*/
static resolve(value) {
return new $Promise((resolve) => resolve(value));
}
static reject(reson) {
return new $Promise((resolve, reject) => reject(reson));
}
/**
*
* @param promise Promise数组
*/
static all(promiseArray) {
const len = promiseArray.length;
// 记录完成的Promise 数量
let succcessIndex = 0;
// 缓存每次完成返回的结果
let successResult = new Array(len);
return new $Promise((resolve, reject) => {
// 如果是var的话,那就需要使用到闭包记录当前循环的索引
for (let index = 0; index < len; index++) {
const element = promiseArray[index];
// 问题 如果 element 不是 Promise 类型怎么操作
element
.then((res) => {
successResult[index] = res;
succcessIndex += 1;
if (succcessIndex === len) {
resolve(successResult);
}
})
.catch(reject);
}
});
}
}
如有错误请联系,请大神勿喷,个人学习总结体会
// 执行顺序面试题
// 微任务也是先是按顺序执行
// 同步任务完成后立即执行该微任务回调
Promise.resolve().then(() => {
//异步代码
console.log(1);
});
// Promise 对象是 pending 状态 // 同步任务
new Promise((resolve, reject) => {
console.log('我是PromiseNew立即执行 2');
resolve();
})
.then(() => {
// Promise Pending Promise 成功后 then的函数才会加到微任务回调队列里
console.log('我是new 执行then里的console 3');
// return new Promise((resolve, reject)=>{
// console.log('我是new 执行then里的new Promise 4 ')
// resolve()
// }) //
// 如果返回的是 Promsie 那么源码执行时,会增加一个 then 的回调
//等同于 return Promise.resolve(1)
return new Promise(resolve=>resolve(1)).then(res=>{
console.log('我要打印的值:3的new Promise',res);
}) // Promise.resolve().then(()=>{4})
// newPromise 根据返回值Promise的状态决定
// Promise.resolve().then(()=>newPromise)
})
.then(
(res) => {
// 同步代码,T1 Pending 状态
console.log('我是new 执行then里的then 5',res);
},
(err) => {
console.log('我是new 执行then里的then 5 err');
return 555;
}
)
.then((res) => {
console.log('我是new 执行then里的then 6', res);
})
.catch((res) => {
console.log('我是new 执行then里的catch ', res);
});
// 将函数放置到微任务队列中
// Promise.resolve()同步代码 fulfilled 成功状态
Promise.resolve()
.then((res) => {
// T2
console.log('我是resolve里的console 7');
// console.log(2222)
})
.then(() => {
// Promise
console.log('我是resolve里的console 7 的then console 8');
});
执行结果
function test2() {
return new Promise(function P1(resolve) {
console.log(1); //1
new Promise(function P2(resolve) {
console.log(2); //2
setTimeout(function set1() {
//处理setTimeOut
resolve(3);
console.log(4); //4
});
}).then(function then1(res) {
setTimeout(function set2() {
//处理setTimeOut
console.log(5);
});
console.log(res); //3
});
setTimeout(function set2() {
// 处理setTimeOut
resolve(6);
console.log(7); //7
});
}).then(function then2(res) {
// 等待
console.log(res); // 6
setTimeout(function set3() {
// 处理setTimeOut
console.log(8);
});
console.log(9); // 9
});
}
执行结果