前言

javascript从诞生之日起就是一门单线程的非阻塞的脚本语言。

非阻塞就是当代码需要进行一项异步任务(无法立刻返回结果,需要花一定时间才能返回的任务,如ajax事件)时,主线程会挂起(pending)这个任务,然后在异步任务返回结果的时候再根据一定规则去执行相应的回调。

javascript引擎到底是如何实现的这一点呢?

1.执行栈

存放当前正在执行的代码的地方被称为执行栈

当我们调用一个方法的时候,js会生成一个与这个方法对应的执行环境(context),又叫执行上下文。这个context存放着这个方法的私有作用域,上层作用域的指向,方法的参数,这个作用域中定义的变量以及这个作用域的this对象。

  1. 当一个脚本第一次执行的时候,js引擎会解析这段代码,并将其中的同步代码按照执行顺序加入栈中,然后从头开始执行。
  2. 如果当前执行的是一个方法,那么js会向执行栈中添加这个方法的context,然后进入这个context继续执行其中的代码。
  3. 当这个context中的代码 执行完毕并返回结果后,js会退出这个context并把这个context销毁,回到上一个方法的context。(函数的相互调用,就像一个栈!)
  4. 这个过程反复进行,直到执行栈中的代码全部执行完毕。

这个过程可以是无限进行下去的,除非发生了栈溢出,即超过了所能使用内存的最大值。

2.事件循环队列

  1. 执行栈遇到一个异步任务后(如ajax请求)并不会一直等待其执行完毕,而是继续执行 栈中的后续任务,这就是js的异步非阻塞特性。
  2. 当一个异步事件完成后,js会将这个事件的回调任务放入事件队列中,而不会影响当前的执行栈。
  3. 当前执行栈中的所有任务都执行完毕时,事件队列中如果有任务的话,主线程会从中取出最优先的任务,放入执行栈中执行。
  4. 如此反复,每次执行栈执行完毕,就去任务队列中查找,这样就形成了一个无限的循环;这就是所谓的 event loop 。
  5. 注意:当所有任务都执行完了(事件队列中也没有任务了),js的运行也不会停止。js宿主(浏览器)每一帧都会执行一次上述的过程,从而保证异步回调会快速响应。可以参见浏览器的requestIdleCallback 或 requestAnimationFrame !

这就是“事件循环(Event Loop)”

经典示例:

setTimeout(() => {
console.log('settimeout')
}, 0)
let start = Date.now();
while (Date.now() - start < 2000);
console.log('finished');
/**
* 运行上面代码,并理解这个过程:
* 执行栈中同步任务先运行2s的循环,然后输出 finished
* 然后取出异步任务执行,输出settimeout
*/

3.异步任务的顺序

不同的异步任务是放在不同的队列里的,当多个队列同时存在待处理的任务时,异步任务之间的执行优先级也有区别!

总体有两种异步任务—— 微观任务(micro task)和宏观任务(macro task)。微观任务总是先于宏观任务被执行!

微观任务是 js引擎自己发出的异步任务,如Promise。  es2015之前,js引擎是无法自己创建异步任务的,所以微观任务是es2015才开始有的概念。

宏观任务是 宿主发起的异步任务,如setTimeout,XMLHttpRequest等 都是宿主window上的对象。

回到上一节,事件循环的第3点:主线程会去查找事件队列是否有任务! 是先取微观任务队列中的任务,直到微观任务队列为空,再找宏观任务队列。

经典示例:

setTimeout(() => {
console.log('settimeout') //宏观异步任务,最后输出
}, 0)
Promise.resolve().then(()=>{
console.log('promise') //微观异步任务,第二个输出
})
console.log('finished') // 同步的,最先输出

异步任务优先级 总体上是这样的顺序,如果要更细节的优先级呢?

微观任务优先级

微观任务目前只有一种,就是Promise创建的,而且微观任务队列只有一个,再加上js是单线程的,即便有多种也很好理解。

Promise.resolve().then(() => {
Promise.resolve().then(() => {
Promise.resolve().then(() => {
console.log('1-1-1')
});
console.log('1-1')
});
console.log('1')
});
Promise.resolve().then(() => {
Promise.resolve().then(() => {
console.log('2-1')
});
console.log('2')
});
// 依次输出: 1 2 1-1 2-1 1-1-1

宏观任务优先级

宏观任务有多种,而且是宿主发起(通常是多线程)又该如何确定顺序呢?

setTimeout(() => {
console.log('timmer')
}, 0);
let start = Date.now();
while (Date.now() - start < 5000); function clickDiv(){ //点击按钮时触发
console.log('click')
}

上面代码很有意思,主线程的同步代码执行需要5s,如果用户在第2s点击了按钮,那么5s后 是先输出 click,还是先输出 timmer 呢?

timmer事件和UI事件,是不同的事件队列,尽管他们都是宏观任务。他们的优先级交给浏览器来决定的 ——  具体宏观任务的优先级,是由具体宿主自己决定的!

所以这个问题请参考具体的宿主资料吧(nodejs 和 浏览器)

JavaScript 的核心机制——event loop(最易懂版)的更多相关文章

  1. 一篇文章图文并茂地带你轻松学完 JavaScript 事件循环机制(event loop)

    JavaScript 事件循环机制 (event loop) 本篇文章已经默认你有了基础的 ES6 和 javascript语法 知识. 本篇文章比较细致,如果已经对同步异步,单线程等概念比较熟悉的读 ...

  2. javascript的执行机制—Event Loop

    既然今天要谈的是javascript的事件循环机制,要理解事件循环,首先要知道事件循环是什么. 我们先从一个例子来看一下javascript的执行顺序. <script> setTimeo ...

  3. JavaScript Concurrency model and Event Loop 并发模型和事件循环机制

    原文地址:https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop JavaScript 有一个基于 event loop 的 ...

  4. Js 运行机制 event loop

    Js - 运行机制 (Even Loop) Javascript 的单线程 - 引用思否的说法: JavaScript的一个语言特性(也是这门语言的核心)就是单线程.什么是单线程呢?简单地说就是同一时 ...

  5. javascript基础修炼(5)—Event Loop(Node.js)

    开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 一. 一道考察异步知识的面试题 题目是这样的,要求写出下面代码的输出: setTimeout(() => { co ...

  6. JavaScript并发模型与Event Loop (转载)

    并发模型可视化描述 model.svg 如上图所示,Javascript执行引擎的主线程运行的时候,产生堆(heap)和栈(stack),程序中代码依次进入栈中等待执行, 若执行时遇到异步方法,该异步 ...

  7. js事件循环机制(Event Loop)

    javascript从诞生之日起就是一门  单线程的  非阻塞的  脚本语言,单线程意味着,javascript代码在执行的任何时候,都只有一个主线程来处理所有的任务,非阻塞靠的就是 event lo ...

  8. js 彻底搞懂事件循环机制 Event Loop

    我们都知道javascript是单线程语言,就是因为单线程的特性,就不得不提js中的同步和异步 一.同步和异步 所谓单线程,无非就是同步队列和异步队列,js代码是自上向下执行的,在主线程中立即执行的就 ...

  9. JavaScript 运行机制 (Event Loop)

    单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务.如果前一个任务耗时很长,后一个任务就不得不一直等着. 所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步 ...

随机推荐

  1. XML-解析失败原因初步分析

    更多精彩文章请关注公众号『大海的BLOG』 首先放出有问题的代码 之所以直入主题是因为肝完了事情,急需入睡.hiahia hiboard:updateUrl="https://xxx.com ...

  2. idea创建springboot工程,总出现响应超时问题,或者无法连接http://start.spring.io导致创建失败

    问题描述如下: idea创建springboot工程,总出现响应超时问题,或者无法连接http://start.spring.io导致创建失败 从我出现此类问题几次的解决方案 依照解决效率分为一下三种 ...

  3. bytectf2019 boring_code的知识学习&&无参数函数执行&&上海市大学生CTF_boring_code+

    参赛感悟 第三次还是第二次参加这种CTF大赛了,感悟和学习也是蛮多的,越发感觉跟大佬的差距明显,但是还是要努力啊,都大三了,也希望出点成绩.比赛中一道WEB都没做出来,唯一有点思路的只有EZCMS,通 ...

  4. java IO流 之 字节流与字符流

    其实学习了file文件基础类,后面的字节流和字符流都特别简单了,首先需要知道字节流和字符流的区别 字节流: 用来传送图片.各种文件.大文件.文本都是通过字节流进行传输的. 字符流: 只能读取文本信息 ...

  5. Java多台中成员访问特点

    多态中的成员访问特点: A:成员变量 编译看左边,运行看左边 B:构造方法 创建子类对象的时候,访问父类的构造方法,对父类的数据进行初始化 C:成员方法 编译看左边,运行看右边.//因为调用对象时,子 ...

  6. spring源码阅读笔记09:循环依赖

    前面的文章一直在研究Spring创建Bean的整个过程,创建一个bean是一个非常复杂的过程,而其中最难以理解的就是对循环依赖的处理,本文就来研究一下spring是如何处理循环依赖的. 1. 什么是循 ...

  7. 2019-2020-1 20199308《Linux内核原理与分析》第四周作业

    <Linux内核分析> 第三章 MenuOS的构造 3.1 Linux内核源代码简介 操作系统的"两把宝剑" 中断上下文:保存现场和恢复现场 进程上下文 目录结构 ar ...

  8. Excel导入异常Cannot get a text value from a numeric cell解决及poi导入时注意事项

    POI操作Excel时偶尔会出现Cannot get a text value from a numeric cell的异常错误. 异常原因:Excel数据Cell有不同的类型,当我们试图从一个数字类 ...

  9. 自动获取时间html代码

    <button type="button" onclick="document.getElementById('demo').innerHTML = Date()& ...

  10. 使用 html5 FileReader 获取图片, 并异步上传到服务器 (不使用 iframe)

    为什么80%的码农都做不了架构师?>>>   原理: 1.使用FileReader 读取图片的base64编码 2.使用ajax,把图片的base64编码post到服务器. 3.根据 ...