掌握JavaScript中的Promise,实现异步编程

事件循环 基本介绍

JavaScript是一门单线程的编程语言,所以没有真正意义上的并行特性。

为了协调事件处理、页面交互、脚本调用、UI渲染、网络请求等行为对主线程造成的影响,事件循环(event loop)方案应运而生。

事件循环说白了就是一个不断的在等待任务、执行任务的方案。

在JavaScript中,根据执行方式的不同,有2种状态的任务,分别是同步任务和异步任务。

同步任务率先执行,而后执行异步任务,所有的异步任务由2个队列存储,分别是:

微任务队列

宏任务队列

主线程在执行完同步任务后,会不断的从这2个任务队列中按照先进先出的策略取出异步任务并执行。

并且在此期间也会有新的事件不断的加入至各个任务队列中,以此循环往复、永不阻塞。

如下图所示:

image-20210811155721716

任务分类

宏任务包括:

setInterval

setTimeout

setTimmediate Node.Js独有

XHR callbackfn

event callbackfn

requestAnimationFrame

UI rendering

微任务包括:

Promise.then

catch finally

process.nextTick Node.Js独有

MutationObserver

执行顺序

根据任务的状态,任务的执行优先级也会有所不同,具体执行顺序如下所示:

同步任务(sync-task)

微任务(micro-task)

宏任务(macro-task)

而关于微任务和宏任务的执行,还有更详细的划分:

微任务队列中一旦有任务,将全部执行完成后再执行宏任务

宏任务队列中的任务在执行完成后,会检查微任务队列中是否有新添加的任务,如果有,那么将执行微任务队列中所有新添加的任务,如果没有则继续执行下一个宏任务

如下图所示:

image-20210811161300458

代码测试:

"use strict"; // 宏任务,每5s添加一个微任务并执行 setInterval(() => { async function foo() { return "micro-task" } async function bar() { let result = await foo(); console.log(result); } bar(); }, 5000); // 宏任务,每1s执行一次 setInterval(() => { console.log("macro-task"); }, 1000); // 同步任务 (() => { console.log("hello world"); })();

测试结果,虽然同步任务的代码在最下面,但是它会最先执行,而每添加一个微任务时,宏任务的执行会被插队:

image-20210811163008045

Promise 认识Promise

Promise是ES6中出现的新功能,用于在JavaScript中更加简单的实现异步编程。

我们可以使用new Promise()创建出一个Promise对象,它接收一个执行器函数,该函数需要指定resolve和reject参数用于改变当前Promise对象的执行状态。

由于Promise对象中执行器代码是属于同步任务,所以他会率先的进行执行,一个Promise对象拥有以下几种状态:

fulfilled:任务完成、使用resolve改变了任务状态

rejected:任务失败、使用reject改变了任务状态,或任务执行中抛出了异常

pending:正在等待、未使用resolve或reject改变任务状态

注意,每个Promise对象的状态只允许改变一次!不可以多次更改。

示例如下。

1)Promise中执行器任务是同步任务,所以会率先执行:

"use strict"; setInterval(() => { console.log("macro task 3"); }, 1000) let task = new Promise((resolve, reject) => { console.log("sync task 1"); }); console.log("sync task 2"); // sync task 1 // sync task 2 // macro task 3

2)使用resolve改变Promise对象的状态为fulfilled:

"use strict"; let task = new Promise((resolve, reject) => { let x = Math.floor(Math.random() * 100) + 1; let y = Math.floor(Math.random() * 100) + 1; let result = x + y; // 返回结果为resolve()中的值 resolve(result); }); console.log(task); // Promise {<fulfilled>: 83}

3)使用reject改变Promise对象的状态为rejected, 它将引发一个异常:

"use strict"; let task = new Promise((resolve, reject) => { let x = Math.floor(Math.random() * 100) + 1; let y = Math.floor(Math.random() * 100) + 1; let result = x + y; // 返回结果为reject()中的值 reject("error!") }); console.log(task); // Promise {<rejected>: "error!"} // Uncaught (in promise) error!

4)如果未使用resolve或reject改变Promise对象状态,那么该任务的状态将为pending:

"use strict"; let task = new Promise((resolve, reject) => { let x = Math.floor(Math.random() * 100) + 1; let y = Math.floor(Math.random() * 100) + 1; let result = x + y; }); console.log(task); // Promise {<pending>} then()

我们可以在Promise对象后,添加一个用于处理任务状态的回调then()方法。

then()方法只有在Promise对象状态为fulfilled或者rejected时才会进行执行,它具有2个参数,接收2个回调函数:

onfulfilled:Promise对象状态为fulfilled将执行该函数,具有1个参数value,接收Promise任务中resolve()所传递的值

onrejected:Promise对象状态为rejected将执行该函数,具有1个参数reason,接收Promise任务中reject()或异常发生时所传递的值

此外,then()方法是属于微任务,所以他会插在宏任务之前进行执行。

image-20210812154335003

代码示例如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyxwjz.html