zoukankan      html  css  js  c++  java
  • Js 运行机制 event loop

    Js - 运行机制 (Even Loop)

    Javascript 的单线程 - 引用思否的说法:

    JavaScript的一个语言特性(也是这门语言的核心)就是单线程。什么是单线程呢?简单地说就是同一时间只能做一件事,当有多个任务时,只能按照一个顺序一个完成了再执行下一个。

    那为什么JS是单线程的呢?

    • JS最初被设计用在浏览器中,作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM
    • 如果浏览器中的JS是多线程的,会带来很复杂的同步问题
    • 比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
    • 所以为了避免复杂性,JavaScript从诞生起就是单线程

    为了提高CPU的利用率,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以这个标准并没有改变JavaScript单线程的本质;

    任务队列 Task queue

    在Javascript中,所有的任务分为两类:同步任务和异步任务

    同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;

    异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

    简要说明:在这里说到了   ‘主线程’   和    ‘任务队列’ ,个人简单理解: 主线程就是 Js 执行的线程 , 任务队列是异步任务暂时存放的一个事件队列;

    在Js执行中,同步任务和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入 Event Table 并注册函数。 

    当指定的事情完成时,Event Table  会将这个函数移入 Event Queue (事件队列)。

    主线程内的任务执行完毕为空,会去 Event Queue 读取对应的函数,进入主线程执行。

    上述过程会不断重复,也就是我们常说的 Event Loop(事件循环)。

    通过上边的描述,我们来看一张图更加清晰的了解  Event Loop (事件循环) 机制

    接下来我们看一个例子:

            setTimeout(function(){
                console.log('1')
            });
     
            new Promise(function(resolve){
                console.log('2');
                resolve();
            }).then(function(){
                console.log('3')
            });
     
            console.log('4');       

    首先setTimeout 是异步进入 事件队列,然后 promise 的 then 也是异步 进入事件队列 ,

    那么按照我们上边说的Js执行机制,先走主线程的同步任务,打印2 ,然后4,紧跟着执行异步任务,也就是任务队列打印1,随后是 3 , 所以结果应该是 2,4,1,3,  事实真的是这样子嘛 ?  接着往下看 : 

    Js 中的宏任务和微任务 - 略记一下

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

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

    process.nextTick(callback)类似node.js版的"setTimeout",在事件循环的下一次循环中调用 callback 回调函数)

    我们上边的  setTimeout  放到了 event queue 事件队列里 , promise 的 then 函数 也被放到了 event queue 事件队列里,然而杯具来了,这两个 queue 并不是一个队列;

    在 Js  Event Loop 机制中

    Promise 执行器中的代码会被主线程同步调用,但是 promise 的回调函数是基于微任务的

    宏任务的优先级高于微任务

    每一个宏任务执行完毕都必须将当前的微任务队列清空

    emmmm~~

    现在我们回到上边的例子中,因为 settimeout 是宏任务,虽然先执行的它,但是他被放到了宏任务的 event queue 里面,然后代码继续往下检查看有没有微任务,检测到 Promise 的 then 函数把它放入了微任务队列。等到主线进程的所有代码执行结束后。先从微任务

    queue 里拿回调函数,然后微任务queue空了后再从宏任务的queue拿函数。

    所以正确的执行结果当然是:2,4,3,1 ;

    由此延申一下  事循环-宏任务-微任务  (Event Queue - Macro - Micro )关系图:

    最后出一个小试题,看看大家是否真的理解到了 Js 的运行机制

    试题借鉴  ssssyoki    答案及分析请前往 ssssyoki 博客。

    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')
        })
    })
  • 相关阅读:
    QFramework 使用指南 2020(二):下载与版本介绍
    QFramework 使用指南 2020 (一): 概述
    Unity 游戏框架搭建 2018 (二) 单例的模板与最佳实践
    Unity 游戏框架搭建 2018 (一) 架构、框架与 QFramework 简介
    Unity 游戏框架搭建 2017 (二十三) 重构小工具 Platform
    Unity 游戏框架搭建 2017 (二十二) 简易引用计数器
    Unity 游戏框架搭建 2017 (二十一) 使用对象池时的一些细节
    你确定你会写 Dockerfile 吗?
    小白学 Python 爬虫(8):网页基础
    老司机大型车祸现场
  • 原文地址:https://www.cnblogs.com/hai-cheng/p/9271777.html
Copyright © 2011-2022 走看看