美文网首页
js执行规则

js执行规则

作者: 青色琉璃 | 来源:发表于2020-04-14 16:21 被阅读0次

    Q1:注册函数时会发生什么?

    名字会写进event table里面

    Q2:js的异步

    js是一门单线程语言,全部的实现异步的方法,都是用同步去模拟的。这一点灰常重要!

    Q3:事件循环Event Loop

    这实际上就是js的执行机制了,也就是js实现异步的方法。

    Q4:js的执行和运行

    执行和运行区别很大,js在不同的环境下,如node,浏览器,Ringo等,执行方式是不同的。但是运行大多指js解析引擎,是统一的。

    1,关于javascript

    h5中提出了Web-Worker,但是js单线程这一点仍未改变,所谓的多线程都是用单线程模拟出来的!

    2,Event Loop事件循环

    js分同步任务和异步任务。

    当我们打开网站时,网页的渲染过程就是一大堆同步任务,比如页面骨架和页面元素的渲染。而像加载图片音乐之类占用资源大耗时久的任务,就是异步任务。

    (画图不易,且画且珍惜)


    1586852433.png

    如何判断主线程为空?

    js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。

    
    let data = [];
    
    $.ajax({
    
        url:www.javascript.com,
    
        data:data,
    
        success:() => {
    
            console.log('发送成功!');
    
        }
    
    })
    
    console.log('代码执行结束');
    

    分析:
    (1) Event Table注册success函数
    (2) 执行console.log('代码执行结束');
    (3) ajax完成,success函数进入Event Queue
    (4) 执行success函数

    3,setTimeOut

    
    function task(){
    
        console.log('定时器')
    
    }
    
    function sleep(delay) {
    
    var start = (new Date()).getTime();
    
    while((new Date()).getTime() - start < delay) {
    
        continue;
    
      }
    
    }
    
    console.log('开始')
    
    setTimeout(() => {
    
        task
    
    }, 3000)
    
    sleep(4000);
    
    console.log('睡眠之后')
    

    分析:

    (1)console.log('开始'),task进入Event Table

    (2)执行sleep函数,主线程会卡在这儿,直到函数执行完成

    (3)3秒之后,计时时间timeout结束,task进入Event Queue,但是sleep函数仍旧在执行,继续等待。

    (4)4秒之后,console.log('睡眠之后');task进入主线程执行,结束。

    4,setInterval

    执行规则与setTimeOut类似,对于setInterval(fn,ms)来说,每隔ms时间,就会将fn推入Event Queue中,而不是每隔ms时间,就执行fn,这也意味着,如果fn的执行时间大于ms,那么就完全看不出有时间间隔了!!

    5,Promise和process.nextTick(callback)

    process.nextTick(callback)类似于node版的setTimeOut,在事件循环的下一次循环中调用callback。

    续上回,除了广义的同步和异步任务,我们还有更精细的任务划分:

    macro-task(宏任务):包括整体代码片段script,setTimeOut,setInterval

    micro-task(微任务):Promise和process.nextTick

    不同的任务类型会进入不同的Event Queue,比如setTimeOut,setInterval会进入相同的Event Queue。

    时间循环的顺序,决定js的执行顺序。代码开始之后,从整体代码片段(宏任务)开始执行,同时将setTimeOut,setInterval中的函数置入Event Table中,随后执行微任务;下一轮开始,宏任务Event Queue开始执行,之后执行微任务。举个栗子:

    
    setTimeout(function() {
    
        console.log('setTimeout');
    
    })
    
    new Promise(function(resolve) {
    
        console.log('promise');
    
        resolve();
    
    }).then(function() {
    
        console.log('then');
    
    })
    
    console.log('console');
    

    分析:

    这段代码作为宏任务,进入主线程。

    遇到setTimeout,将其回调函数注册后置入Event Queue。

    遇到new Promise,立即执行console.log('promise')。then函数进入微任务Event Queue。

    console.log('console');

    第一个宏任务结束,检查微任务,执行then函数。

    第一轮结束,开始第二轮。宏任务中setTimeout回调函数执行。

    结束

    关系图如下所示(抠图不自赏)


    1586852481.png

    下面,上主菜:

    
    console.log('1');
    
    setTimeout(function() {
    
        console.log('2');
    
        process.nextTick(function() {
    
            console.log('3');
    
        })
    
        new Promise(function(resolve) {
    
            console.log('4');
    
            resolve();
    
        }).then(function() {
    
            console.log('5')
    
        })
    
    })
    
    process.nextTick(function() {
    
        console.log('6');
    
    })
    
    new Promise(function(resolve) {
    
        console.log('7');
    
        resolve();
    
    }).then(function() {
    
        console.log('8')
    
    })
    
    setTimeout(function() {
    
        console.log('9');
    
        process.nextTick(function() {
    
            console.log('10');
    
        })
    
        new Promise(function(resolve) {
    
            console.log('11');
    
            resolve();
    
        }).then(function() {
    
            console.log('12')
    
        })
    
    })
    

    分析:

    • 第一轮开始,进入整体代码片段script,开始宏任务。console.log('1');

    • setTimeout,其回调函数注册,置入宏任务Event Queue,记为setTimeout1。

    • process.nextTick,其回调函数注册,置入微任务Event Queue,记为process1。

    • new Promise,立即执行console.log('7'),then函数置入微任务Event Queue,记为then1.

    • 又是一个setTimeout,其回调函数注册,置入宏任务Event Queue,记为setTimeout2。

    • 查看微任务Event Queue,发现process1和then1.

    • 执行process1,输出6

    • 执行then1,输出8

    • 第一轮输出 1,7,6,8

    第二轮开始

    • 查看宏任务Event Queue,发现setTimeout1和setTimeout2.

    • 执行setTimeout1。console.log('2')。

    • process.nextTick,其回调函数注册,置入微任务Event Queue,记为process2。

    • new Promise,立即执行console.log('4');then函数置入微任务Event Queue,记为then2.

    • 宏任务结束,查看微任务,发现process2和then2。

    • 执行process2,输出3。

    • 执行then2,输出5.

    第二轮输出2,4,3,5。开始第三轮

    • 执行setTimeout2。

    • console.log('9')。

    • process.nextTick,其回调函数注册,置入微任务Event Queue,记为process3。

    • new Promise,立即执行console.log('11');then函数置入微任务Event Queue,记为then3.

    • 宏任务结束,查看微任务,发现process3和then3。

    • 执行process2,输出10。

    • 执行then2,输出12.

    第三轮输出9,11,10,12

    三轮结束,最后输出1,7,6,8,2,4,3,5,9,11,10,12。

    (请注意,node环境下的事件监听依赖libuv与前端环境不完全相同,输出顺序可能会有误差)

    6,总结

    微任务和宏任务还有很多种类,比如setImmediate等等,执行都是有共同点的,有兴趣的同学可以自行了解

    javascript是一门单线程语言

    Event Loop是javascript的执行机制

    原文链接:https://juejin.im/post/59e85eebf265da430d571f89

    相关文章

      网友评论

          本文标题:js执行规则

          本文链接:https://www.haomeiwen.com/subject/ntetvhtx.html