zoukankan      html  css  js  c++  java
  • JavaScript执行运行时 再解(一)

    浏览器拿到一段 JavaScript 代码,是如何运行的?

    设想一下,如果你是chrome 浏览器的开发者(这些人是真的666),如何使用Java

    Script 引擎执行 JavaScript?

    其实当浏览器收到一段 JavaScript 的代码的时候,浏览器首先做的,就是传递给 JavaScript 引擎,让它去执行。

    然而,执行 JavaScript 并不是一锤子的买卖。宿主环境会把 JavaScript 传给 JavaScript 引擎去运行,此外,还有可能会通过 API 把 JavaScript 代码传递给 JavaScript 引擎,比如 setTimeOut,比如 new promise。它会允许 JavaScript 在特定的时机执行。

    所以,我们首先应该形成一个感性的认知:一个 JavaScript 引擎会常驻在内存里面,等待我们(宿主)把 JavaScript 传递给引擎来执行。

    我们都知道,JavaScript 是单线程的语言,在早起的版本里面没有异步执行代码的能力,这就意味着,宿主环境传递给 JavaScript 引擎一段代码,引擎就把代码直接顺序执行了,倘若遇到一个 alert(),那代码岂不就是卡死在 alert 那里。

    但是在 ES5 之后,JavaScript 加入了 Promise,这个时候,不需要浏览器的安排,JavaScript 引擎本身也可以发起任务了。

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

    宏观任务和微观任务

    在 winter 老师前端进阶训练营第四周里面,就用了 OC 语言来模拟一个 JavaScript 引擎,自己不会 OC,看的都不是太懂。

    不过,

    while(TRUE) {
    	r = wait;
    	execute(r);
    }
    

    整个循环做的事情就是反复的“等待-执行”,一直循环。

    我们大概这么理解:宏观任务的队列就相当于事件循环。

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

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

    Promise 之前博客里面有写过详细的用法。

    直接看代码

        var r = new Promise(function(resolve, reject){
            console.log("a");
            resolve()
        });
        r.then(() => console.log("c"));
        console.log("b")
    		// a b c
    
        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
    

    这段代码里面,无论代码的顺序是什么样子的,d 一定发生在 c 之后,因为 Promise 产生的是 JavaScript 引擎内部的微任务,而setTimeout 是浏览器的 API,它产生的是宏任务。

    为了理解微任务始终先于宏任务,我们设计一个实验,执行一个耗时1 秒的 Promise

        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"))
        });
    

    这里我们强制了 1 秒的执行耗时,这样,我们就可以确保任务 c2 是在 d 之后被添加到任务队列的。

    我们可以看到,即使耗时一秒的 c1 执行完毕,再 enque 的 c2,仍然在 d 之前执行了,这很好的解释了微任务优先的原理。

    666,以后什么 Promise 题不会做呀。。。

    通过一系列的实现,我们可以总结一下如何分析异步执行的顺序:

    1. 首先我们分析有多少个宏任务
    2. 在每个宏任务里面有多少个微任务
    3. 根据调用次序,确定宏任务中的微任务执行次序
    4. 根据宏任务的触发规则和调用次序,确定宏任务的执行次序
    5. 确定整个次序

    再夸一遍,666,以后什么 Promise 题不会做呀。。。(winter 666)

    我们再来看一个稍微复杂的例子:

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

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

    首先我们来看,setTimeout 把整个代码分成了两个宏观任务,这里无论是 5 秒还是 50 秒还是 50min 还是 0秒,都是一样的。(写夸张点 2333)。

    第一个宏观任务当中,包含了先后同步执行的 console.log(“a”) 和 console.log(“b”)。

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

    Promise 是 JavaScript 中的一个定义,但是实际编写代码的时候,我们可以发现,它似乎并不比回调的方式书写更加简单。但是从 ES6 开始,我们有了 async/await,这个语法改进跟 Promise 配合,能够有效地改善代码结构。

    新特性:async/await

    要好好看下 async/await,因为自己就用过 yield

    async/await 是 ES2016 新加入的特性(ES2015 就是我们所说的 ES6,ES2016 就是 ES6 更新后的一个版本,不知道正确与否,我是这么理解的。)

    它提供了 for、if 等代码结构来编写异步的方式。它的运行时基础是 Promise,面对这种比较新的特性,我们先来看一下基本用法。

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

    async 函数时一种特殊语法,特征是在 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")
    }
    

    这段代码利用了我们之定义的 sleep 函数。在异步函数 foo 中,我们调用 sleep。

    async 函数的强大之处在于,它是可以嵌套的。我们在定义了一大批原子操作的情况下,可以使用 async 函数组合出新的 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 也常常跟被拿来跟异步一起来讲,然而实质上这并非异步代码,只是在缺少 async/await 的时候,一些框架用这样的特性来模拟 async/await。

    有了 async/iterator,generator/iterator模拟的用法也应该被飞起。

    作业:实现一个红绿灯,把一个圆形 div 按照绿色 3 秒、黄色 1 秒、红色 2 秒循环改变背景色。

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
      </head>
      <body>
        <div
          class="cycle"
          style="
            background-color: red;
            height: 500px;
             500px;
            border-radius: 100%;
            margin: 0 auto;
          "
        >
          666
        </div>
        <script>
          var x = document.querySelector('div');
          x.style.backgroundColor = 'black';
          function sleep(duration) {
            return new Promise((resolve, reject) => {
              setTimeout(resolve, duration);
            });
          }
          async function changeCircleColor(duration, color) {
            x.style.backgroundColor = color;
            await sleep(duration);
          }
          (async function main() {
            while (true) {
              await changeCircleColor(300, 'green');
              await changeCircleColor(200, 'red');
              await changeCircleColor(100, 'yellow');
            }
          })();
        </script>
      </body>
    </html>
    
  • 相关阅读:
    Hadoop综合大作业
    hive基本操作与应用
    理解MapReduce计算构架
    熟悉HBase基本操作
    Hadoop综合大作业
    hive基本操作与应用
    理解MapReduce计算构架
    熟悉HBase基本操作
    熟悉常用的HDFS操作
    爬虫大作业
  • 原文地址:https://www.cnblogs.com/ssaylo/p/13152858.html
Copyright © 2011-2022 走看看