博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript:并发模型与Event Loop
阅读量:7045 次
发布时间:2019-06-28

本文共 2107 字,大约阅读时间需要 7 分钟。

一、JavaScript的单线程

众所周知,JavaScript的一大特点就是单线程,但是我们有没有思考过它为什么不能是多线程的?

我们假定JavaScript有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?所以为了避免这种复杂性,从一诞生,JavaScript就是单线程。

尽管HTML5提出Web Worker,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,并没有改变JavaScript单线程的本质。

二、定时器

定时器主要是setTimeout()和setInterval()这两个函数,这也是平时编程时候用到最多的。

console.log(1);setTimeout(function() {    console.log(2);},5000);console.log(3);

上面代码的执行结果是1,3,2。但如果将setTimeout()的第二个参数设为0,就表示当前代码执行完以后,立即执行(0毫秒延迟)指定的回调函数。setTimeout(fn,0)的含义是,它在任务队列的尾部添加一个事件,在主线程最早得到空闲时去执行,也就是说,尽可能早得执行。

需要注意的是,定时器只是将事件插入了任务队列,必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。如果当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。这也引申出JavaScript的并发模型

三、并发模型

我们先看一下理论上的并发模型:

图片描述

栈(stack):函数调用会形成了一个堆栈帧

堆(heap):对象被分配在一个堆中,一个用以表示一个内存中大的未被组织的区域
队列(queue):运行时包含的一个待处理的消息队列。当栈为空时,则从队列中取出一个消息进行处理。这个处理过程包含了调用与这个消息相关联的函数(以及因而创建了一个初始堆栈帧)

四、Event Loop

针对上面的并发模型和JavaScript的同步异步运行机制,我们可以看到整个流程大致是这样的:

1.所有同步任务都在主线程上执行,形成一个执行栈(并发模型的stack)。

2.主线程之外,还存在一个任务队列(并发模型的queue)。只要异步任务有了运行结果,就在任务队列中放置一个事件。
3.一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面有哪些事件和那些对应的异步任务,于是等待结束状态,进入执行栈,开始执行。
4.主线程不断重复上面的第三步。

这个过程是循环不断的,所以这种运行机制又称为Event Loop(事件循环)。放一张大神演讲时的图片来更好地理解Event Loop:

图片描述

我们可以看到,主线程运行的时候,产生(heap)和(stack),中的代码调用各种外部API,它们在任务队列中加入各种事件(click,load,done)。只要中的代码执行完毕,主线程就会去读取任务队列,依次执行那些事件所对应的回调函数。

五、Macrotask 和 Microtask

这是一个比较冷门的知识,在并发模型中队列又可以分为Macrotask 和 Microtask,它们都属于异步任务。先来看一个例子:

console.log('script start');setTimeout(function() {    console.log('setTimeout');}, 0);Promise.resolve().then(function() {    console.log('promise1');    setTimeout(function() {        console.log('setTimeout in microtask');    }, 0);}).then(function() {    console.log('promise2');});console.log('script end');

输出:

script start

script end
promise1
promise2
setTimeout
setTimeout in microtask

Macrotask 和 Microtask有什么区别呢?

  • Macrotasks:setTimeout, setInterval, setImmediate, I/O, UI rendering
  • Microtask:process.nextTick, Promises, Object.observe(废弃), MutationObserver

它们的执行过程如下:

JavaScript引擎首先从macrotask queue中取出第一个任务

执行完毕后,将microtask queue中的所有任务取出,按顺序全部执行
然后再从macrotask queue中取下一个
执行完毕后,再次将microtask queue中的全部取出
循环往复,直到两个queue中的任务都取完

转载地址:http://zahal.baihongyu.com/

你可能感兴趣的文章
linux网络编程
查看>>
自己实现简单的AOP(一)简介
查看>>
java.util.Map源码分析
查看>>
2018/11/29 算法时空(2) 算法导论第三章 函数的增长
查看>>
2017-2018-1 JAVA实验站 冲刺 day05
查看>>
关于控制台输出 警告 log4j:WARN No appenders could be found for logger
查看>>
xshell代理设置
查看>>
八大排序算法
查看>>
FatMouse' Trade 贪心
查看>>
22个所见即所得在线 Web 编辑器
查看>>
Windows7(x86) xampp php5.5 imagick install
查看>>
开发 MFC 应用的一般过程
查看>>
zkw费用流模板
查看>>
baocms7.0版本一元云购报错Call-time pass-by-reference has been removed处理办法
查看>>
PHPexcel 基本操作
查看>>
线程模型
查看>>
二、安装配置
查看>>
C# 7.0 新特性
查看>>
POSTMAN and HTTPie to test APIs
查看>>
总结系列_13(Qt使用总结,续...)
查看>>