基本使用
事件循环Event Loop的机制介绍
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
- 代码执行首先会进入一个宏任务。因为script的执行就是一个宏任务,会将script任务的指向放入调用栈主线程,然后查找同步任务进行执行。
- 同步任务执行完毕,查找当前宏任务中的微任务如promise,将微任务放入调用栈进行执行。执行完同步任务和微任务,本次宏任务才算执行完毕,将当前宏任务的执行出栈。
- 两次宏任务之间,有可能进行UI渲染更新。
- 开始检查下次的宏任务,即异步任务。然后再次按照宏任务内部的指向顺序依次执行。
Promise的三种状态
- pending
- resolved
- rejected
状态的变化只能是由pending=>resolved或者pending=>rejected
const p1 = new Promise((resolve, reject) => {});
console.log("p1", p1); // <pending>
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 0);
});
console.log("p2", p2); //<pending>
setTimeout(() => console.log("p2", p2), 10); // <fulfilled>
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 0);
});
console.log("p3", p3); //<pending>
setTimeout(() => console.log("p3", p3), 10); // <rejected>
- pending状态不会触发then或catch
- resolved会触发后续then的回调函数执行
- rejected会触发后续catch的回调函数执行
```javascript
const p4 = Promise.resolve(100);
console.log(“p4”, p4); //
: 100 p4.then((data) => { console.log(data); // 100 }).catch((err) => { console.log(“err”, err); });
const p5 = Promise.reject(“reject err”);
console.log(“p5”, p5); //
<a name="ixMG4"></a>
### then和catch状态改变
- then正常返回resolved,里面有Error则返回rejected
- catch正常返回resolved,里面有Error则返回rejected
```javascript
const p1= Promise.resolve(100).then(()=>{
return 100
})
p1.then((data)=>{
console.log('data',data) //没有Error,是resolved,触发then回调
}).catch(err=>{
console.error('err', err);
})
const p2= Promise.resolve(100).then(()=>{
throw new Error('then error')
})
p2.then((data)=>{
console.log('data',data)
}).catch(err=>{
console.error('p2-catch-err', err);// 有Error,是rejected,触发catch回调
})
const p3 = Promise.reject("reject-error").catch(err=>{
console.error("p3 catch-err",err)
})
p3.then(()=>{
console.log('p3') //没有Error,是resolved,触发then回调
}).catch(err=>{
console.error("p3-err",err)
})
const p4 = Promise.reject("reject-error").catch(()=>{
throw new Error('p4 catch error')
})
p4.then((data)=>{
console.log('p4',data)
}).catch(err=>{
console.error("p4-err",err) // 有Error,是rejected,触发catch回调
})
相关面试题
Promise.resolve().then(()=>{
console.log(1) //1
}).catch(()=>{
console.log(2)
}).then(()=>{
console.log(3) //3
})
// 打印输出1、 3
Promise.resolve().then(()=>{
console.log(1) // 1
throw new Error("message: error")
}).catch(()=>{
console.log(2) //2
}).then(()=>{
console.log(3) //3
})
//打印输出1、 2 、3
Promise.resolve().then(()=>{
console.log(1) //1
throw new Error("message: error")
}).catch(()=>{ // 这里没有报错,返回的就是resolved
console.log(2) //2
}).catch(()=>{
console.log(3)
})
// 打印输出1、 2
使用promise,实现红3s、绿1s、黄灯2s依次交替亮灯。
let red=()=>{
console.log("red light")
}
let green=()=>{
console.log("green light")
}
let yellow=()=>{
console.log("yellow light")
}
// 一开始亮绿灯,绿灯3秒后,亮黄灯1s,然后亮红灯3秒,接着绿灯
let run = ()=>{
new Promise((res,rej)=>{
setTimeout(()=>{
res(yellow())
},3000)
}).then(()=>{
setTimeout(()=>{
red()
},1000)
}).then((res,rej)=>{
setTimeout(()=>{
green()
},5000)
})
}
green();
run();
setInterval(()=>{
run()
},9000)
线上完成代码:https://codepen.io/shenshuai/pen/XWRMaNN
源码分析
1处理传入的参数必须是函数,不是函数报错
class NPromise{
constructor(handler){
// 判断传入的类型,必须是函数,否则报错
if(typeof handler !== "function"){
throw new TypeError("handler must be a function")
}
handler(this._resolve, this._reject)
}
_resolve(){
console.log("object");
}
_reject(){
console.log("_reject");
}
}
2promise 的三个状态pending/resolve/reject
初始化状态为pending,状态的转化只能从pending到resolve,或者pending到reject。状态转换后,就不能再转化。使用status标记状态转化
class NPromise{
static PENDING = "PENDING";
static RESOLVED = "RESOLVED";
static REJECTED = "REJECTED";
constructor(handler){
if(typeof handler !== "function"){
throw new TypeError("handler must be a function")
}
handler(this._resolve.bind(this), this._reject.bind(this))
this.status = NPromise.PENDING;
}
_resolve(){
// 避免已经执行了reject,再次执行resolve,当状态不为pending,则退出执行
if(this.status !== NPromise.PENDING) return;
this.status = NPromise.RESOLVED;
console.log("object", this);
}
_reject(){
// 避免已经执行了resolve,再次执行reject,当状态不为pending,则退出执行
if(this.status !== NPromise.PENDING) return;
this.status = NPromise.REJECTED;
console.log("_reject", this.status);
}
}
3then执行事件
通过设置事件队列,目的将then中的事件依次执行;
- this.resolveQueue = [];
this.rejectQueue = [];
class NPromise{
static PENDING = "PENDING";
static RESOLVED = "RESOLVED";
static REJECTED = "REJECTED";
constructor(handler){
if(typeof handler !== "function"){
throw new TypeError("handler must be a function")
}
this.status = NPromise.PENDING;
this.resolveQueue = [];
this.rejectQueue = [];
handler(this._resolve.bind(this), this._reject.bind(this))
}
_resolve(value){
// 避免已经执行了reject,再次执行resolve,当状态不为pending,则退出执行
if(this.status !== NPromise.PENDING) return;
this.status = NPromise.RESOLVED;
let handler;
//依次取出队列中的事件任务,队列的任务执行,先进入先执行,所以使用shift从头获取
while(handler = this.resolveQueue.shift()){
handler(value)
}
}
_reject(value){
// 避免已经执行了resolve,再次执行reject,当状态不为pending,则退出执行
if(this.status !== NPromise.PENDING) return;
this.status = NPromise.REJECTED;
let handler;
while(handler = this.rejectQueue.shift()){
handler(value)
}
}
then(resHandler, rejHandler) {
this.resolveQueue.push(resHandler);
this.rejectQueue.push(rejHandler);
}
}
4promise的resolve和reject执行时机,微任务执行
如果promise的resolve或reject不在异步中包含,则执行顺序会出现问题。所以要用异步微任务处理,浏览器用postMessage,node用nextTick;
_resolve(value){
// 避免已经执行了reject,再次执行resolve,当状态不为pending,则退出执行
if(this.status !== NPromise.PENDING) return;
this.status = NPromise.RESOLVED;
let handler;
// 浏览器环境
if(typeof window !== "undefined"){
window.addEventListener("message",()=>{
while(handler = this.resolveQueue.shift()){
handler(value)
}
})
window.postMessage("message")
}else{
process.nextTick(() => {
while(handler = this.resolveQueue.shift()){
handler(value)
}
})
}
}
_reject(value){
// 避免已经执行了resolve,再次执行reject,当状态不为pending,则退出执行
if(this.status !== NPromise.PENDING) return;
this.status = NPromise.REJECTED;
let handler;
if(typeof window !== "undefined"){
window.addEventListener("message",()=>{
while(handler = this.rejectQueue.shift()){
handler(value)
}
})
window.postMessage("message")
}else{
process.nextTick(() => {
while(handler = this.rejectQueue.shift()){
handler(value)
}
})
}
}
5then返回值
返回值是promise,才可以链式调用
then(resHandler, rejHandler) {
// this.resolveQueue.push(resHandler);
// this.rejectQueue.push(rejHandler);
return new NPromise((resolve, reject) => {
function handlerResolvedEvent(value) {
let resResult = resHandler(value);
if(resResult instanceof NPromise){
// 这里是promise,需要用then调用函数
resResult.then(resolve, reject);
}else{
resolve(value)
}
}
function handlerRejectedEvent(value) {
let rejResult = rejHandler(value);
if(rejResult instanceof NPromise){
// 这里是promise,需要用then调用函数
rejResult.then(resolve, reject);
}else{
reject(value);
}
}
this.resolveQueue.push(handlerResolvedEvent);
this.rejectQueue.push(handlerRejectedEvent);
});
}
6catch/finally静态方法
catch方法是then的reject的另一种写法,对then方法需要进行改造,判断第一个参数是否存在,如果不存在则直接执行后续内容。
then(resHandler, rejHandler) {
// this.resolveQueue.push(resHandler);
// this.rejectQueue.push(rejHandler);
return new NPromise((resolve, reject) => {
function handlerResolvedEvent(value) {
// 判断resHandler是否是函数,即是否存在,对catch进行兼容
if(typeof resHandler === 'function'){
let resResult = resHandler(value);
if(resResult instanceof NPromise){
// 这里是promise,需要用then调用函数
resResult.then(resolve, reject);
}else{
resolve(resResult)
}
}else{
resolve(value)
}
}
function handlerRejectedEvent(value) {
if(typeof rejHandler === 'function'){
let rejResult = rejHandler(value);
if(rejResult instanceof NPromise){
// 这里是promise,需要用then调用函数
rejResult.then(resolve, reject);
}else{
reject(rejResult);
}
}else{
reject(value);
}
}
this.resolveQueue.push(handlerResolvedEvent);
this.rejectQueue.push(handlerRejectedEvent);
});
}
catch(reject){
return this.then(undefined, reject);
}
finally方法执行,需要独立设置一个finallyQueue队列,在resolve或reject中都执行
constructor(handler){
if(typeof handler !== "function"){
throw new TypeError("handler must be a function")
}
this.status = NPromise.PENDING;
this.resolveQueue = [];
this.rejectQueue = [];
this.finallyQueue = [];
handler(this._resolve.bind(this), this._reject.bind(this))
}
_resolve(value){
// 避免已经执行了reject,再次执行resolve,当状态不为pending,则退出执行
if(this.status !== NPromise.PENDING) return;
this.status = NPromise.RESOLVED;
console.log("_resolve object", this);
let handler;
// 浏览器环境
if(typeof window !== "undefined"){
window.addEventListener("message",()=>{
while(handler = this.resolveQueue.shift()){
handler(value)
}
})
window.postMessage("message")
}else{
process.nextTick(() => {
while(handler = this.resolveQueue.shift()){
handler(value)
}
})
}
this._finally(value)
}
_reject(value){
// 避免已经执行了resolve,再次执行reject,当状态不为pending,则退出执行
if(this.status !== NPromise.PENDING) return;
this.status = NPromise.REJECTED;
let handler;
if(typeof window !== "undefined"){
window.addEventListener("message",()=>{
while(handler = this.rejectQueue.shift()){
handler(value)
}
})
window.postMessage("message")
}else{
process.nextTick(() => {
while(handler = this.rejectQueue.shift()){
handler(value)
}
})
}
this._finally(value)
}
_finally(value){
let handler;
if(typeof window !== "undefined"){
window.addEventListener("message",()=>{
while(handler = this.finallyQueue.shift()){
handler(value)
}
})
window.postMessage("message")
}else{
process.nextTick(() => {
while(handler = this.finallyQueue.shift()){
handler(value)
}
})
}
}
finally(event){
this.finallyQueue.push(event);
}
7其他静态方法处理
静态方法,resolve、reject、all、race、allSetlledstatic resolve(value){
return new NPromise((resolve, reject)=>{
resolve(value);
})
}
static reject(value){
return new NPromise((resolve, reject)=>{
reject(value);
})
}
static all(iterator){
let len = iterator.length;
let i = 0;
let runTasks = [];
return new NPromise((resolve, reject) =>{
iterator.forEach(task => {
task.then((res,rej)=>{
i++;
runTasks.push(res);
if(i === len){
resolve(runTasks);
}
}).catch(err=>{
reject(err)
})
});
})
}
static race(iterator){
return new NPromise((resolve, reject) =>{
iterator.forEach(task =>{
task.then(res=>{
resolve(res)
}).catch(err=>{
reject(err)
})
})
})
}
static allSetlled(iterator){
let len = iterator.length;
let i = 0;
let runTasks = [];
return new NPromise((resolve, reject) =>{
iterator.forEach(task=>{
task.then(res=>{
i++;
runTasks.push(res);
if(i===len){
resolve(runTasks)
}
}).catch(err=>{
i++;
runTasks.push(err);
if(i===len){
resolve(runTasks)
}
})
})
})
}