zoukankan      html  css  js  c++  java
  • [javascript]并发模型与事件循环(Concurrency model and Event loop)

    JavaScript有一个基于事件循环的并发模型,事件循环负责执行代码,收集和处理事件以及执行队列中的子任务。
    并发模型与事件循环(Concurrency model and Event loop)
    包含对 栈 堆 队列的理解

    运行时(runtime)概念

    可视化描述

    Frame 帧;Stack 栈;Heap 堆;Queue 队列

    函数调用形成了一个由若干帧组成的栈,比如:

    function foo(b) {
        let a = 10 ;
        return a + b + 11;
    }
    
    function bar(x) {
        let y = 3 ;
        return foo(x * y);
    }
    
    console.log(bar(7));// 42
    

    当调用bar时,将创建包含bar的参数和局部变量的第一帧。当bar调用foo时,会创建包含foo参数和局部变量的第二帧,并将其推到的第一个帧之上。当foo返回时,顶部的帧元素从堆栈中弹出(只留下bar的调用帧)。 当bar返回时,最下面的帧也被弹出,此时堆栈为空。

    对象Object被分配到一个堆中,堆只是一个名称,用来表示内存中的一个大区域(大多数是非结构化的)

    队列(先进先出)

    JavaScript运行时包含了一个待处理消息的消息队列,每一个消息都关联着一个用以处理这个消息的回调函数。
    在事件循环期间的某个时刻,运行时会从最先进入队列的消息开始处理队列中的消息。被处理的消息会被移出队列,并作为输入参数来调用与之关联的函数。
    正如前面所提到的,调用一个函数总是会为其创建一个新的栈帧。

    函数的处理会一直进行到执行栈再次为空为止;然后事件循环将会处理队列中的下一个消息(如果还有的话)

    事件循环

    之所以成为“事件循环”,是因为它经常按照类似如下的方式被实现

    while (queue.waitForMessage()) {
        queue.processNextMessage();
    }
    

    queue.waitForMessage()会同步地等待消息到达(如果当前没有任何消息等待被处理)

    执行至完成(Run-to-completion)

    每个消息完整地执行后,其它消息才会被执行。这为程序的分析提供了一些优秀的特性,包括:当一个函数执行时,它不会被抢占,只有在它运行完毕之后才回去运行任何其他的代码,才能修改这个函数操作的数据。

    这个模型的一个缺点在于当一个消息需要太长时间才能处理完毕时,web应用程序无法处理与用户的交互,例如点击或者滚动。为了缓解这个问题,浏览器一般会弹出一个“这个脚本运行时间过长”的对话框。
    一个良好的习惯是缩短单个消息处理时间,并在可能的情况下将一个消息裁剪成多个消息。

    添加消息

    在浏览器里,每当一个事件发生并且有一个事件监听器绑定在该事件上时,一个消息就会被添加进消息队列。如果没有事件监听器,这个事件将会丢失。
    所以当一个带有点击事件处理器的元素被点击时,就会像其他事件一样产生一个类似的消息。

    函数setTimeout接受两个参数:待加入队列的消息和一个时间值(可选,默认为0)。这个时间值代表了消息被实际加入到队列的最小延迟时间。
    如果队列中没有其它消息并且栈为空,在这段延迟时间过去之后,消息会被马上处理。但是如果有其它消息,setTimeout消息必须等待其它消息处理完。
    因此,第二个参数仅仅表示最少延迟时间,而非确切等待时间。(品,细品

    下面例子演示了这个概念(setTimeout并不会在计时器到期之后直接执行):

    const s = new Date().getSeconds();
    
    setTimeout(function() {
        console.log("Run after" + (new Date().getSeconds() - s) + "seconds");
    },500)//500ms
    
    while(true) {
        if(new Date().getSeconds() - s >= 2) {
            console.log("Good, loop for 2 seconds");
            break;
        }
    }
    
    //输出
    //Good, looped for 2 seconds
    //console.log("Good, looped for 2 seconds"); //说明 setTimeout的回调函数并不是在500ms之后立即执行
    
    

    零延迟

    零延迟实际上并不意味着回调在零毫秒之后就会发出。调用延迟为0(零)毫秒的setTimeout不会在给定的时间间隔之后执行回调函数。
    执行取决于队列中等待任务的数量。
    在下面的示例中,消息"this is just a message"将在处理回调中的消息之前写入控制台,因为延迟是运行时处理请求所需的最短时间(不是保证的时间)。
    基本上,即使您为setTimeout指定了特定的时间限制,setTimeout也需要等待队列消息的所有代码完成后再执行回调。

    (function() {
        console.log('this is the start');
    
        setTimeout(function cb() {
            console.log('Callback 1: this is a msg from callback');
        })//has a default time value of 0
    
        console.log('this is just a message');
    
        setTimeout(function cb1() {
            console.log('Callback 2: this is a msg from call back');
        }, 0);
    
        console.log('this is the end');
    })
    //输出顺序
    //this is the start
    //this is just a message
    //this is the end
    //Callback 1: this is a msg from call back
    //Callback 2: this is a msg from call back
    

    多个运行时一起通信 (Several runtimes communicating together)

    Web工作者或跨源iframe有自己的堆栈、堆和消息队列。
    两个不同的运行时只能通过postMessage方法发送消息进行通信。
    如果其他运行时侦听消息事件,则此方法将消息添加到其他运行时

    永不阻塞

    JavaScript的事件循环模型与许多其他语言不同的一个非常有趣的特性是,它永不阻塞。
    处理I/O通常通过事件和回调来执行,所以当一个应用正等待一个IndexedDB查询返回或者一个XHR请求返回时,它仍然可以处理其它事情,比如用户输入。

    由于历史原因有些例外,如alert或者同步XHR,但应该尽量避免使用它们例外

    参考学习链接:
    https://www.jianshu.com/p/600804b1486b

    坚持,坚持,坚持。再坚持坚持!
  • 相关阅读:
    把我给另外一个朋友的炒股劝告发给你一遍,希望你可以得到帮助!
    Microsoft Office Document Imaging批量ocr 方法
    Advanced GET 9.1 修正汉化版(免注册、页面加载、保存都正常)
    波浪分析数据转换:大智慧、钱龙、胜龙可用Advanced GET ToGet 数据转换器V3.05特别版
    怎样精确计算股市主力的持仓量
    混沌理论
    地产联盟内部资料总目录
    C语言 · P1001(大数乘法)
    C语言 · 排列数
    C语言 · 逆序排列
  • 原文地址:https://www.cnblogs.com/danker/p/12660173.html
Copyright © 2011-2022 走看看