最近看了3天Promise,终于把Promise看懂了。以下为自己整理的Promise介绍和如何实现Promise的then/catch/resolve/reject及所有Promsie方法。(本文适合有一定js基础的人观看,代码详细解释有空我再补充) 以及Promise.all/Promise.race/Promise.allSettled/Promsie.any/Promise.retry的实现方法。
Promise介绍
特点:
是个构造函数,接收一个函数类型的参数。原型链上存在then/catch方法,then/catch方法也返回promise。支持异步执行,支持链式调用。
Promise对象有三种状态:
pending进行中、fulfilled已成功、rejected已失败
then方法:
promise.then((value) => {}, (error) => {}) 可接收两个回调函数作为参数;第一个回调是Promise对象状态变为resolved时调用,第二个回调是状态变为rejected时调用;这俩函数都是可选的。
catch方法:
Promise.prototype,catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名。
Promise.all([p1, p2, p3]):
都成功才会成功,有一个失败就会变成rejected
Promise.race() :
哪个最快就返回哪个的结果,不管别的执没执行完成
Promise.allSettled() ES2020(ES11)
所有请求都结束。参数数组的所有Promise对象都发生状态变更(不管是fulfilled还是rejected),返回的Promise对象才会发生状态变更。 该方法返回新的Promise实例,一旦发生状态变更,状态总是fulfilled,不会变成rejected。 返回结果: 异步操作成功{status: 'fulfilled', value: value};异步操作失败{status: 'rejected', reason: reason} Promise.allSettled([p1, p2, p3]).then(res => { console.log('res=', res) }) // [{status: 'rejected', reason: 11}, ......]
ES2021: Promise.any()
只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态,若参数实例都变成rejected状态,包装实例就会变成rejected状态。 和Promise.race不同点:不会因为某个promise变成rejected状态而结束,必须等到所有promise变成rejected状态才会结束。 总结:有一个成功就会成功,所有失败才会失败 AggregateError: All promises were rejected
手写Promise及相关内容
function MyPromise() { }
Promise.resolve() / Promise.reject()
MyPromise.prototype.then = function () { }
MyPromise.prototype.catch = function () { }
MyPromise.prototype.all = function () { }
MyPromise.prototype.race= function () { }
MyPromise.prototype.allSettled= function () { }
MyPromise.prototype.any= function () { }
MyPromise.prototype.retry= function () { } (扩展)
实现一个Promise流程
- 第一步:依然是执行new Promise中的同步代码,我们加了两个数组 that.onfufilledCallback = [];that.onrejectedCallback = [];来缓存then函数的handleFulfilled方法, handleRejected方法
- 第二步:执行 executor(resolve, reject);的时候遇到setTimeout,那就先等等再来执行setTimeout里面的东西吧
- 第三步:执行第一个then吧
- 第四步:在then函数里面有new MyPromise执行吧
- 第五步:执行then函数中的executor(resolve, reject),同步执行当前的状态是pending,并且将当前handleFulfilled, handleRejected缓存了下来
- 第六步:执行第二个then函数,操作过程同上一个then,依次将第二个then的handleFulfilled缓存下来
- 第七步:第三个then执行,缓存
实现一个Promise:
function MyPromise(executor) {
this.status = "pending"; //初始状态
this.success = undefined; // 成功的值
this.error = undefined; // 失败的原因
this.onfufilledCallback = [];
this.onrejectedCallback = [];
let resolve = (val) => {
if (val instanceof MyPromise) {
return val.then(resolve, reject);
}
queueMicrotask(() => {
// 模拟异步任务
if (this.status == "pending") {
this.status = "fufilled";
this.success = val; //保存当前成功的返回值
this.onfufilledCallback.forEach((resolvefn) => {
resolvefn(this.success);
});
}
});
};
let reject = (val) => {
queueMicrotask(() => {
if (this.status == "pending") {
this.status = "rejected";
this.error = val;
this.onrejectedCallback.forEach((rejectfn) => {
rejectfn(this.error);
}); //同步执行的时候缓存
}
});
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
} // 立即执行;若executor执行报错,直接执行reject
}
promise.then实现
MyPromise.prototype.then = function (handleFulfilled, handleRejected) {
handleFulfilled =
typeof handleFulfilled == "function"
? handleFulfilled
: (data) => {
return data;
}; // 解决promise穿透现象
handleRejected =
typeof handleRejected == "function"
? handleRejected
: (data) => {
throw data;
};
let that = this;
let mypromise2 = new MyPromise((resolve, reject) => {
if (that.status == "fufilled") {
try {
let res = handleFulfilled(this.success); //成功的值
resolve(res); //不管上一个then是成功还是失败第二个then都可以接收到上面返回的值
} catch (e) {
reject(e);
}
}
if (that.status == "rejected") {
try {
let err = handleRejected(this.error); //失败的值
resolve(err); //不管上一个then是成功还是失败第二个then都可以接收到上面返回的值
} catch (e) {
reject(e);
}
}
if (that.status == "pending") {
//当函数调用的时候,去执行数组里面的函数
that.onfufilledCallback.push(() => {
try {
let res = handleFulfilled(this.success);
resolve(res); //不管上一个then是成功还是失败第二个then都可以接收到上面返回的值
} catch (e) {
reject(e);
}
}); //这里的onfufulled就相当于then里面的第一个参数回调
that.onrejectedCallback.push(() => {
try {
let res = handleRejected(this.error);
resolve(res); //不管上一个then是成功还是失败第二个then都可以接收到上面返回的值
} catch (e) {
reject(e);
}
});
}
});
return mypromise2;
};
promise.catch实现
MyPromise.prototype.catch = function (callback) {
return this.then(function () { }, callback)
}
Promise.resolve实现
Promise.resolve = function (param) {
if (param instanceof MyPromise) {
// 如果传入的是个promise类型,直接执行
return param;
}
return new MyPromise((resolve) => resolve(param));
};
Promise.reject实现
MyPromise.reject = (reason) => {
return new MyPromise((resolve, reject) => reject(reason))
}
Promise.all([p1, 22, p3])实现
MyPromise.all = function (values) {
let num = 0;
let arr = [];
return new MyPromise((resolve, reject) => {
for (let i = 0; i < values.length; i++) {
let current = values[i];
if (current.then && typeof current.then === "function") {
// 传入的是promise,要执行promise并返回结果
current.then.call(
current,
(val) => {
arr[i] = val; // 跟数组传参顺序一致
if (++num === values.length) {
// 考虑异步
resolve(arr);
}
},
reject
);
} else {
// 传入的是具体的值
arr[i] = current;
if (++num === values.length) {
resolve(arr);
}
}
}
});
};
使用示例:
let promise1 = new MyPromise((resolve, reject) => {
setTimeout(() => { resolve(111) }, 2000)
})
let promise3 = new MyPromise((resolve, reject) => {
setTimeout(() => { resolve(3333) }, 2000)
})
MyPromise.all([promise1, 22, promise3]).then(res => { console.log('res==', res) })
Promise.race([p1,222,p3]) 执行完一个得到一个状态就输出
MyPromise.race = function (values) {
return new MyPromise(function (resolve, reject) {
try {
let num = 0;
let arr = [];
let current;
for (let i = 0; i < values.length; i++) {
current = values[i];
if (current.then && typeof current.then === "function") {
current.then(resolve, reject);
} else {
resolve(current); // 若是普通值,直接抛出
}
}
} catch (e) {
reject(e);
}
});
};
Promise.allsettled([]): 所有参数都执行完 [{status: 'rejected', reason: 11}, ......]
Promise.myAllSettled = function (values) {
let num = 0;
let arr = [];
return new Promise((resolve, reject) => {
for (let i = 0; i < values.length; i++) {
Promise.resolve(values[i]).then(
(res) => {
arr[i] = { status: "fulfilled", value: res };
if (++num === values.length) {
resolve(arr);
}
},
(err) => {
arr[i] = { status: "rejected", value: err };
if (++num === values.length) {
resolve(arr);
}
}
);
}
});
};
**Promise.any([]) ** 全失败才会失败AggregateError: All promises were rejected,有一个成功就会成功
Promise.myAny = function (values) {
let num = 0;
let arr = [];
for (let i = 0; i < values.length; i++) {
Promise.resolve(values[i])
.then((res) => {
resolve(res);
})
.catch((err) => {
arr[i] = err;
if (++num === values.length) {
reject("AggregateError: All promises were rejected");
}
});
}
};
Promise的retry(重试)功能实现:一个方法失败后间隔delay秒再重试,重试超过times次后,报错
Promise.retry = (getData, times, delay) => {
return new Promise((resolve, reject) => {
function attemp() {
getData()
.then(resolve)
.catch((err) => {
if (times === 0) {
reject(err);
} else {
times--;
setTimeout(attemp(), delay);
}
});
}
attempt();
});
};
另附一道面试题:
MyPromise.resolve().then(() => {
console.log(0);
return MyPromise.resolve(4);
}).then((res) => {
console.log(res)
})
MyPromise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(5);
}).then(() =>{
console.log(6);
}) // 输出:0124356 若换成原生Promise,则0123456
原因:js引擎为了让微任务尽快输出,做了一些优化,连续多个then(3个)若没有reject或resolve,会交替执行then,而不至于让一个堵太久,不单单v8这样其他引擎也这样,其实promise内部状态已经结束。这块在v8源码里有完成体现。