【JavaScript】——“重学前端”05JavaScript执行——Promise里的代码为什么比setTimeout先执行?

当拿到一段 JavaScript 代码时,浏览器或者 Node 环境首先要做的就是;传递给JavaScript 引擎,并且要求它去执行。

执行 JavaScript 并非一锤子买卖,宿主环境当遇到一些事件时,会继续把一段代码传递给 JavaScript 引擎去执行,此外,我们可能还会提供 API 给 JavaScript 引擎,比如setTimeout 这样的 API,它会允许 JavaScript 在特定的时机执行。

一个 JavaScript 引擎会常驻于内存中,它等待着我们(宿主)把 JavaScript 代码或者函数传递给它执行。

我们把宿主发起的任务称为宏观任务,把 JavaScript 引擎发起的任务称为微观任务

这里每次的执行过程,其实都是一个宏观任务。我们可以大概理解:宏观任务的队列就相当于事件循环。

在宏观任务中,JavaScript 的 Promise 还会产生异步代码,JavaScript 必须保证这些异步代码在一个宏观任务中完成,因此,每个宏观任务中又包含了一个微观任务队列

Promise 是 JavaScript 语言提供的一种标准化的异步管理方式。

它的总体思想是,需要进行 io、等待或者其它异步操作的函数,不返回真实结果,而返回一个“承诺”,函数的调用方可以在合适的时机,选择等待这个承诺兑现(通过 Promise 的 then 方法的回调)。


Promise 的基本用法示例如下:

 function sleep(duration) {
     return new Promise(function(resolve, reject) {
     setTimeout(resolve,duration);
     })
 }
 sleep(1000).then( ()=> console.log("finished"));

函数 sleep的作用是等候传入参数指定的时长。

微任务始终先于宏任务

总结一下如何分析异步执行的顺序:
        首先我们分析有多少个宏任务;
        在每个宏任务中,分析有多少个微任务;
        根据调用次序,确定宏任务中的微任务执行次序;
        根据宏任务的触发规则和调用次序,确定宏任务的执行次序;
        确定整个顺序。

稍微复杂的例子:

function sleep(duration) {
     return new Promise(function(resolve, reject) {
         console.log("b");
         setTimeout(resolve,duration);
     })
 }
 console.log("a");
 sleep(5000).then(()=>console.log("c"));

//执行顺序 :a、b、c

这是一段非常常用的封装方法,利用 Promise 把 setTimeout 封装成可以用于异步的函数。

第一个宏观任务中,包含了先后同步执行的 console.log(“a”); 和 console.log(“b”);。
setTimeout 后,第二个宏观任务执行调用了 resolve,然后 then 中的代码异步得到执
行,所以调用了 console.log(“c”),最终输出的顺序才是: a b c。

新特性:async/await: 

async/await的运行时基础是 Promise。

async 函数必定返回 Promise,我们把所有返回 Promise 的函数都可以认为是异步函数

在 function 关键字之前加上 async 关键字,这样,就定义了一个 async 函数,我们可以在其中使用 await 来等待一个 Promise。

function sleep(duration) {
     return new Promise(function(resolve, reject) {
         setTimeout(resolve,duration);
     })
}
async function foo(){
     console.log("a")
     await sleep(2000)
     console.log("b")
}

async 函数强大之处在于,它是可以嵌套的。

function sleep(duration) {
     return new Promise(function(resolve, reject) {
         setTimeout(resolve,duration);
     })
}
async function foo(name){
     await sleep(2000)
     console.log(name)
}
async function foo2(){
     await foo("a");
     await foo("b");
}

这里 foo2 用 await 调用了两次异步函数 foo,可以看到,如果我们把 sleep 这样的异步操作放入某一个框架或者库中,使用者几乎不需要了解 Promise 的概念即可进行异步编程了。

generator/iterator 也常常被跟异步一起来讲,我们必须说明 generator/iterator并非异步代码,只是在缺少 async/await 的时候,一些框架(最著名的要数 co)使用这样的特性来模拟 async/await。

所以有了 async/await 之后,generator/iterator 来模拟异步的方法应该被废弃。

猜你喜欢

转载自blog.csdn.net/qq_50497708/article/details/128196900
今日推荐