基本使用
事件循环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```javascriptconst 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) // 1throw new Error("message: error")}).catch(()=>{console.log(2) //2}).then(()=>{console.log(3) //3})//打印输出1、 2 、3
Promise.resolve().then(()=>{console.log(1) //1throw new Error("message: error")}).catch(()=>{ // 这里没有报错,返回的就是resolvedconsole.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)}})})})}
