• js执行机制


    JavaScript的运行机制

    (1)所有同步任务都在主线程上执行,形成一个执行栈。

    (2)主线程之外,还存在"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

    主线程自上而下执行所有代码,同步任务直接进入到主线程被执行,而异步任务则进入到 Event Table 并注册相对应的回调函数,异步任务完成后,Event Table 会将这个函数移入 Event Queue,主线程任务执行完了以后,会从Event Queue中读取任务,进入到主线程去执行。

    (3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

    (4)主线程不断重复上面的第三步

    上述动作不断循环,就是我们所说的事件循环(Event Loop)。

    一个事件循环中有一个或者是多个任务队列

    JavaScript中有两种异步任务:

    宏任务(MacroTask)、微任务(MicroTask)

    JavaScript 的任务不仅仅分为同步任务和异步任务,同时从另一个维度,也分为了宏任务(MacroTask)和微任务(MicroTask)。

    宏任务,所有的同步任务代码都是MacroTask(这么说其实不是很严谨,下面解释),setTimeout、setInterval、I/O、UI Rendering 等都是宏任务。

    微任务,为什么说上述不严谨我却还是强调所有的同步任务都是 MacroTask 呢,因为我们仅仅需要记住几个 MicroTask 即可,排除法!别的都是 MacroTaskMicroTask 包括:Process.nextTick、Promise.then catch finally(注意我不是说 Promise)、MutationObserver。


    例1:
    setTimeout(()=>{
        console.log("setTimeout1");
        Promise.resolve().then(data => {
            console.log(222);
        });
    });
    setTimeout(()=>{
        console.log("setTimeout2");
    });
    Promise.resolve().then(data=>{
        console.log(111);
    });
    Promise.resolve().then(data=>{
        console.log(222);
    });
    

    运行结果为:

    111
    setTimeout1
    222
    setTimeout2
    

    我们来详细说明一下, JS引擎是如何执行这段代码的:

    1. 主线程上没有需要执行的代码
    2. 接着遇到setTimeout 0,它的作用是在 0ms 后将回调函数放到宏任务队列中(这个任务在下一次的事件循环中执行)。
    3. 接着遇到setTimeout 0,它的作用是在 0ms 后将回调函数放到宏任务队列中(这个任务在再下一次的事件循环中执行)。
    4. 首先检查微任务队列, 即 microtask队列,发现此队列不为空,执行第一个promise的then回调,输出 '111'。
    5. 此时microtask队列为空,进入下一个事件循环, 检查宏任务队列,发现有 setTimeout的回调函数,立即执行回调函数输出 'setTimeout1',检查microtask 队列,发现队列不为空,执行promise的then回调,输出'222',microtask队列为空,进入下一个事件循环。
    6. 检查宏任务队列,发现有 setTimeout的回调函数, 立即执行回调函数输出'setTimeout2'。

    例2:
    console.log('script start');
    
    setTimeout(function () {
        console.log('setTimeout---0');
    }, 0);
    
    setTimeout(function () {
        console.log('setTimeout---200');
        setTimeout(function () {
            console.log('inner-setTimeout---0');
        });
        Promise.resolve().then(function () {
            console.log('promise5');
        });
    }, 200);
    
    Promise.resolve().then(function () {
        console.log('promise1');
    }).then(function () {
        console.log('promise2');
    });
    Promise.resolve().then(function () {
        console.log('promise3');
    });
    console.log('script end');
    

    运行结果为:

    script start
    script end
    promise1
    promise3
    promise2
    setTimeout---0
    setTimeout---200
    promise5
    inner-setTimeout---0
    

    我们来详细说明一下, JS引擎是如何执行这段代码的:

    1. 首先顺序执行完主进程上的同步任务,第一句和最后一句的console.log
    2. 接着遇到setTimeout 0,它的作用是在 0ms 后将回调函数放到宏任务队列中(这个任务在下一次的事件循环中执行)。
    3. 接着遇到setTimeout 200,它的作用是在 200ms 后将回调函数放到宏任务队列中(这个任务在再下一次的事件循环中执行)。
    4. 同步任务执行完之后,首先检查微任务队列, 即 microtask队列,发现此队列不为空,执行第一个promise的then回调,输出 'promise1',然后执行第二个promise的then回调,输出'promise3',由于第一个promise的.then()的返回依然是promise,所以第二个.then()会放到microtask队列继续执行,输出 'promise2';
    5. 此时microtask队列为空,进入下一个事件循环, 检查宏任务队列,发现有 setTimeout的回调函数,立即执行回调函数输出 'setTimeout---0',检查microtask 队列,队列为空,进入下一次事件循环.
    6. 检查宏任务队列,发现有 setTimeout的回调函数, 立即执行回调函数输出'setTimeout---200'.
    7. 接着遇到setTimeout 0,它的作用是在 0ms 后将回调函数放到宏任务队列中,检查微任务队列,即 microtask 队列,发现此队列不为空,执行promise的then回调,输出'promise5'。
    8. 此时microtask队列为空,进入下一个事件循环,检查宏任务队列,发现有 setTimeout 的回调函数,立即执行回调函数输出,输出'inner-setTimeout---0'。代码执行结束.

    ### 为什么会需要event-loop

    因为 JavaScript 是单线程的。单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。为了协调事件(event),用户交互(user interaction),脚本(script),渲染(rendering),网络(networking)等,用户代理(user agent)必须使用事件循环(event loops)。

    最后有一点需要注意的是:本文介绍的是浏览器的Event-loop,因此在测试验证时,一定要使用浏览器环境进行测试验证,如果使用了node环境,那么结果不一定是如上所说。

  • 相关阅读:
    java8 parallel并行处理实战
    java相关技术问答(二)
    [安卓基础] 007.管理Activity的生命周期
    [Python基础]009.os模块(1)
    [Objective-C] 012_数据持久化_XML属性列表,NSUserDefaults
    SD.Team团队人物形象
    读Pyqt4教程,带你入门Pyqt4 _013
    [Objective-C] 011_数据持久化_NSKeyedArchiver
    [Objective-C] 010_Foundation框架之NSSet与NSMutableSet
    [JavaWeb基础] 007.Struts2的配置和简单使用
  • 原文地址:https://www.cnblogs.com/w-yh/p/12053644.html
Copyright © 2020-2023  润新知