Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript事件循环(Event Loop)? #8

Open
Rain120 opened this issue Jun 5, 2019 · 0 comments
Open

JavaScript事件循环(Event Loop)? #8

Rain120 opened this issue Jun 5, 2019 · 0 comments

Comments

@Rain120
Copy link
Owner

Rain120 commented Jun 5, 2019

众所周知,Javascript是单线程的脚本语言,也就是说,同一个时间只能做一件事。它的主要用途是与用户互动,以及操作DOM。由于它的用途决定了它只能是单线程的。

举个例子,假定JavaScript同时有两个线程a, b,这两个线程都对一个DOM进行不同的操作,a线程添加内容,b线程删除内容,那浏览器怎么判断呢?即便后来HTML5提出Web Worker来允许Javascript创建多个线程,但本质上还是主线程控制子线程,只是分担主线程的一部分计算任务,并没有改变JavaScript单线程的本质。

同步和异步

同步和异步

同步:在执行某个函数后,能够在执行后直接得到返回结果的操作。

异步:在执行某个函数后,不能够在执行后直接得到返回结果的,而需要通过其他手段才能得到结果的操作。

同步就是串行,异步就是并行。

JavaScript变量类型

  • 基本数据类型(按值访问)
    Number, String, Null, Undefined, Boolean, Symbol(ES6)

  • 引用类型(引用地址访问)

    Object, Array, Function

栈内存(stack)与堆内存(heap)

image

  • 栈stack:自动分配的内存空间,它由系统自动释放,空间小,运行效率高。

    一般基本数据类型存放在栈区,栈遵循后进先出的原则。

  • 堆heap:动态分配的内存,自定义大小,用户主动自动释放,空间大,运行效率低。

    一般引用数据类型存放在堆区。

image

JavaScript并发模型基于“事件循环”, 浏览器提供运行时环境来执行我们的JavaScript代码, 浏览器主要包括 调用堆栈 ,事件循环 ,任务队列 和 Web API 。像setTimeout,setInterval和Promise这样的全局函数不是JavaScript的一部分,而是Web API的一部分。JavaScript环境的可视化表示如下所示:

image

JS调用堆栈是后进先出(LIFO)。引擎一次从堆栈中获取一个函数,并从上到下依次运行代码。每次遇到一些异步代码(如setTimeout)时,它都会将其交给Web API(箭头1)。因此,每当触发事件时,callback都会被发送到任务队列(箭头2)。Event Loop不断监视任务队列,并按照排队顺序一次处理一个callback。每当调用堆栈为空时,循环检索回调并将其放入堆栈(箭头3)进行处理。请记住,如果调用堆栈不为空,则事件循环不会将任何callbacks推送到堆栈。

任务队列(task queue)

事件循环具有一个或多个任务队列。 任务队列是一个有序的任务列表,一个任务队列便是一系列有序任务(task)的集合;每个任务都有一个任务源(task source),源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列。

任务队列是先进先出。

执行栈

JavaScript在主线程执行任务会形成一个执行栈。

JavaScript在执行完执行栈的内容后,会读取任务队列中的任务加入执行栈并依次执行。

宏任务和微任务

在JavaScript中异步任务中分为两种任务,宏任务(macro)task和微任务(microtask),那么我们是如何区分的这两种任务的呢?

宏任务在单个循环周期中一次一个地推入堆栈,但是微任务队列总是在执行返回到event loop(包括任何额外排队的项)之前清空。

宏任务(macrotask):

script(全局任务)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 环境)

微任务(microtask):

Promise、 MutaionObserver、Object.observer,process.nextTick(Node.js环境)

它又是如何执行的呢?

image

在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理的步骤如下:
1、执行栈选择宏任务(script)执行(有就执行,没有就下一步);
2、检查是否存在 Microtasks,如果存在则不停地执行,直至清空 Microtasks Queue;
3、render(每一次事件循环,浏览器都可能会去更新渲染);
4、重复上述步骤。

你还可以通过这个非常棒的可视化工具来理解调用堆栈

参考资料

Tasks, microtasks, queues and schedules

So you think you know JavaScript?

JavaScript 运行机制详解:再谈Event Loop

tasks-microtasks-queues-and-schedules

event-loops

ask-queues

Eventloop不可怕,可怕的是遇上Promise

@Rain120 Rain120 changed the title JavaScript事件循环(Event Loop)? JavaScript事件循环(Event Loop)? Jun 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant