程序员web之路让前端飞

【十一】JavaScript执行(一):Promise里的代码为

2019-02-27  本文已影响3人  alanwhy

首先我们要形成一个感性的认知:一个JS引擎会常驻于内存中,等待着宿主把JS代码或者函数传递给它执行。

在ES3或者更早的版本中,JS本身是没有异步执行代码的能力的,也就是说,宿主环境传递给JS引擎一段代码,引擎就把代码直接顺次执行了,这个任务也就是宿主发起的任务。

但是,ES5之后,JS引入了Promise,如此而来,JS引擎本身也可以发起任务了

那么采纳JSC引擎的术语,我们把宿主发起的任务叫宏观任务,把JS引擎发起的任务叫微观任务。

宏观和微观任务

JS引擎等待宿主环境分配宏观任务,在node术语中,叫做事件循环

整个循环做的事情基本上就是反复“等待-执行”,当然实际的代码中并没有那么简单,还有要判断循环是否结束、微观任务队列等逻辑。

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


image.png

有了宏观任务和微观任务机制,就可以实现JS引擎级和宿主机的任务了,例如:promise永远在队列尾部添加微观任务。setTimeout等宿主API,则会添加宏观任务。

Promise

promise是JS语言提供的一种标准化的异步管理方式,总体思想是,需要进行io、等待或者其他异步操作的函数,不返回真实结果,而返回一个“承诺”,函数的调用方可以在合适的时机,选择等待这个promise兑现(通过promise的then方法回调)

promise的基本用法:

// 等候传入参数指定的时长
    function sleep(duration) {
        return new Promise(function(resolve, reject) {
            setTimeout(resolve,duration);
        })
    }
    sleep(1000).then(()=> console.log("finished"));

promise的then回调是一个异步的执行过程,下面我们就来研究一下promise函数中的执行顺序,例如:

    var r = new Promise(function(resolve, reject){
        console.log("a");
        resolve()
    });
    r.then(() => console.log("c"));
    console.log("b")

输出结果为a b c。在进入console.log("b")之前,r已经得到了resolve,但是promise的resolve始终是异步操作,所以c无法出现在b之前。

在看一个:

    var r = new Promise(function(resolve, reject){
        console.log("a");
        resolve()
    });
    setTimeout(()=>console.log("d"), 0)
    r.then(() => console.log("c"));
    console.log("b")

输出结果为a b c d。因为promise是JS引擎内部的微任务,而setTimeout是浏览器API,是宏任务。

为了便于理解微任务始终先于宏任务,在看一个例子:

    setTimeout(()=>console.log("d"), 0)
    var r = new Promise(function(resolve, reject){
        resolve()
    });
    r.then(() => { 
        var begin = Date.now();
        while(Date.now() - begin < 1000);
        console.log("c1") 
        new Promise(function(resolve, reject){
            resolve()
        }).then(() => console.log("c2"))
    });

执行一个耗时1s的promise。可以确保c2是在d之后被添加到任务队列。

最后总结下如何分析异步执行的顺序:

值得一提,promise是JS中的一个定义,但是实际编写代码时,它似乎并不比毁掉的方式书写简单,但是从ES6开始,我们有了async/await,这个语法改进跟promise配合,能够有效地改善代码结构

async/await

async/await是ES6新加入的特性,提供了for、if等代码结构来编写异步的方式。运行基础是promise。

async函数必定返回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");
}

小练习

需求:实现一个红绿灯,圆形的div按照绿色3s,黄色1s,红色2s循环改变背景色。

代码实现:

const lightEle = document.getElementById('traffic-light');
function changeTrafficLight(color, duration) {
  return new Promise(function(resolve, reject) {
    lightEle.style.background = color;
    setTimeout(resolve, duration);
  })
}

async function trafficScheduler() {
  await changeTrafficLight('green', 3000);
  await changeTrafficLight('yellow', 1000);
  await changeTrafficLight('red', 2000);
  trafficScheduler();
}

trafficScheduler();

参考原文: JavaScript执行(一):Promise里的代码为什么比setTimeout先执行?

上一篇下一篇

猜你喜欢

热点阅读