最近在整理promise的一些知识点,以前看的太浅,如今再去理解又是另外一种感觉,废话就不多说了,直接上原理
Promise原理
promise函数是ES6里的一部分,new promise对象,promise其实也是一个构造函数,自身有reject、resolve、race方法,原型链上有then、catch方法。用new出来一个promise对象会自带这些方法
promise有两个特点
1、 Promise对象的状态不受外界影响
- pending(进行中)
- fulfilled(已成功)
- rejected(已失败)
2、一旦状态改变,就不会再变,任何时候都可以得到这个结果,状态改变,只有两种情况
- 从pending变为fulfilled
- 从pending变为rejected
手动实现Promise(Class vs ES5)
以前都不太喜欢使用class的写法来实现一个功能,直到有一天我翻遍了网上手动实现promise的ES5代码后,觉得难读难理解,代码还不整洁,官方给的实现方法也just so so,偶然我看到一篇使用class写法,真的超级香,很清爽也易读易懂易实现,代码还很优雅,果断记录下来
class MyPromise {
static Pending = "pending";
static Fulfiled = "fulfiled";
static Reject = "reject";
constructor(executor) {
this.status = MyPromise.Pending; // Promise实例的状态,初始化为pending
this.result = undefined; // result保存异步操作成功时的返回值
this.error = undefined; // error保存异步操作失败时的返回值
this.callbacks = []; // 保存then方法传入的回调函数,在实例状态改变时调用
// 执行传入的函数,为入参resolve和reject绑定this
console.log("执行传入的函数");
executor(this._resolve.bind(this), this._reject.bind(this));
}
// 实现resolve函数
_resolve(result) {
if (result instanceof MyPromise) {
// 表示onSuccess返回结果是一个Promise实例,假定这个实例为a
// 我们是想获取这个叫a的Promise实例里面的result,但是a实例并没有暴露任何获取result的方法
// 但是a实例的then方法里有个成功的回调函数,这个回调函数里可以取到result。
result.then(this._resolve.bind(this), this._reject.bind(this));
return;
}
this.status = MyPromise.Fulfiled;
this.result = result;
this.callbacks.forEach((cb) => {
this._handle(cb);
});
}
// 实现reject函数
_reject(error) {
if (error instanceof MyPromise) {
error.then(this._resolve.bind(this), this._reject.bind(this));
}
this.status = MyPromise.Reject;
this.error = error;
this.callbacks.forEach((cb) => {
this._handle(cb);
});
}
// then方法把传入的回调函数保存起来,当状态改变时调用。
then(onSuccess, onError) {
// 把下一个Promise实例的resolve方法也存进callbasks中去
// 是为了把当前onSuccess的执行结果通过resolve方法赋给下一个Promise实例的result
return new MyPromise((nextResolve, nextReject) => {
this._handle({
nextResolve,
nextReject,
onSuccess,
onError,
});
});
}
catch(onCatch) {
return this.then(undefined, onCatch);
}
finally(onFinally) {
return this.then(onFinally, onFinally);
}
_handle(cb) {
const { nextResolve, nextReject, onSuccess, onError } = cb;
if (this.status === MyPromise.Pending) {
this.callbacks.push(cb);
}
if (this.status === MyPromise.Fulfiled) {
const nextResult = onSuccess ? onSuccess(this.result) : undefined;
nextResolve(nextResult);
return;
}
if (this.status === MyPromise.Reject) {
// 注意这里修改了
const nextError = onError ? onError(this.error) : this.error;
nextReject(nextError);
return;
}
}
}
const p = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject("Hello Promise");
}, 1000);
});
// 调用第一个then方法之后,会创建下一个Promise对象。
// 会把当前Promise对象的成功回调函数、失败回调函数、下一个Resolve方法、下一个Reject方法存起来。
// 然后在调用onSuccess之后,把其返回值作为nextResolve的参数,赋给下一个Promise对象的result1,
// 这样第二个then方法的成功回调函数就会取到result1,以此类推。
p.then((res) => {
console.log("res==========", res);
})
.catch((err) => {
console.log("err==========", err);
})
.finally(() => {
console.log("finally");
});
或许我如获至宝的感觉说出来太平缓了,那就再来看看JS实现的promise,代码冗长且难记,亲自做下对比
// 定义_Promise构造函数
function _Promise(fn) {
this.status = "pending"; // 定义属性存储状态 // 赋予初始状态pending
this.value = null; // resolve的value
this.reason = null; // reject的reason
this.onFulfilled = []; // 存储then方法中注册的第一个回调函数
this.onReject = []; // 存储then方法中注册的第二个回调函数
var handler = (funct) => {
// 事件处理函数
if (typeof funct === "function") {
// 如果是函数的话才进行执行
if (this.status === "fulfilled") funct(this.value); // 执行并传递value
if (this.status === "rejected") funct(this.reason); // 执行并传递rejected
}
};
// 实现resolve回调
var resolve = (value) => {
// 使用箭头函数主要是为了绑定this指向
this.status = "fulfilled"; // 设置状态
this.value = value; // 得到结果
if (value instanceof _Promise) {
// 判断返回的值是否为Promise实例
value.onFulfilled = this.onFulfilled; // 是则以此链式调用
return value;
}
setTimeout(() => {
// 使用setTimeout是为了将回调函数置于任务队列,不阻塞主线程,异步执行,实际promise的回调是置于微队列的,而setTimeout的回调是置于宏队列
try {
this.onFulfilled.forEach(handler); // 交予事件处理
} catch (e) {
console.error(`Error in promise: ${e}`); // 打印异常
reject(e); // reject
}
}, 0);
};
// 实现rejected
var reject = (reason) => {
// 使用箭头函数主要是为了绑定this指向
this.status = "rejected"; // 设置状态
this.reason = reason; // 得到结果
setTimeout(() => {
// 置于任务队列
try {
this.onReject.forEach(handler); // 交予事件处理
} catch (e) {
console.error(`Error in promise: ${e}`); // 打印异常
}
}, 0);
};
fn(resolve, reject); // 执行
}
// 定义then
// value接收上层结果 --- function处理自身逻辑 --- return传递到下层
_Promise.prototype.then = function (onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v; // 转为函数
onRejected = typeof onRejected === "function" ? onRejected : (r) => r; // 转为函数
return new _Promise((resolve, reject) => {
// 返回一个新的_Promise
this.onFulfilled.push((value) => {
// 将回调函数置于onFulfilled
resolve(onFulfilled(value)); // 执行并传递
});
this.onReject.push((value) => {
// 将回调函数置于onReject
reject(onRejected(value)); // 执行并传递
});
});
};
// 测试
var promise = new _Promise(function (resolve, reject) {
var rand = Math.random() * 2;
setTimeout(function () {
if (rand < 1) resolve(rand);
else reject(rand);
}, 1000);
});
promise
.then(
(rand) => {
console.log("resolve", rand); // resolve回调执行
},
(rand) => {
console.log("reject", rand); // reject回调执行
}
)
.then(function () {
// resolve后继续执行
return new _Promise(function (resolve, reject) {
var rand = Math.random() * 2;
setTimeout(function () {
resolve(rand);
}, 1000);
});
})
.then(function (num) {
// resolve后继续执行
console.log(num, "继续执行并接收参数");
return 1;
})
.then(function (num) {
console.log(num, "继续执行并接收参数");
});
/*
实现的_Promise比较简单
实际使用的Promise比较复杂,有各种情况的考虑
例子中仅实现了Promise构造函数与then,实际中还有catch、Promise.all、Promise.race、Promise.resolve、Promise.reject等实现
*/
手动实现promise.all
这是我曾经面试头条的一道题目,归类记录下
const promiseAll=(arr)=>{
let resInfo=[]
return new Promise((resolve,reject)=>{
let i=0
next()
next()=>{
arr[i].then(re=>{
resInfo.push(re)
i++
if(i===arr.length){
resolve(resInfo)
}else{
next()
}
}
}
})
}
let p1=Promise.resolve(1)
let p2=Promise.resolve('hello I am No2')
let p3=Promise.resolve('hello I am No3')
promiseAll([p1,p2,p3]).then(value=>{
console.log(value)
})
手动实现promiseAllSettled
const promiseAllSettled=(arr)=>{
if(Array.isArray(arr)){
throw Error('must Array')
}
return new Promise((resolve)=>{
let count=0
let data=[]
arr[i].then(res=>{
data[i]={
status:'fulfilled',
value:res
}
},err=>{
data[i]={
status:'rejected',
reason:err
}
})
.finally(()=>{
if(++count===len){
resolve(data)
}
})
})
}