09-02.js运行机制:异步和单线程

#前言

面试时,关于同步和异步,可能会问以下问题:

  • 同步和异步的区别是什么?分别举一个同步和异步的例子

  • 一个关于 setTimeout 的笔试题

  • 前端使用异步的场景哪些?

面试时,关于js运行机制,需要注意以下几个问题:

  • 如何理解JS的单线程

  • 什么是任务队列

  • 什么是 EventLoop

  • 理解哪些语句会放入异步任务队列

  • 理解语句放入异步任务队列的时机

#JS的异步和单线程

因为是单线程,所以必须异步。

我们通过题目来解释以下。

#题目一:异步

现有如下代码:

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

上面的代码中,我们很容易知道,打印的顺序是1,3,4,2。因为你会想到,要等一秒之后再打印2

可如果我把延时的时间从1000改成0

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

上方代码中,打印的顺序仍然是1,3,4,2。这是为什么呢?我们来分析一下。

总结:

js 是单线程(同一时间只能做一件事),而且有一个任务队列:全部的同步任务执行完毕后,再来执行异步任务。第一行代码和最后一行代码是同步任务;但是,setTimeout是异步任务。

于是,执行的顺序是:

  • 先执行同步任务console.log(1)

  • 遇到异步任务setTimeout,要挂起

  • 执行同步任务console.log(3)

  • 全部的同步任务执行完毕后,再来执行异步任务console.log(2)

很多人会把这个题目答错,这是因为他们不懂 js 的运行机制。

注意上面那句话:同步任务执行完毕后,再来执行异步任务。也就是说,如果同步任务没有执行完,异步任务是不会执行的。为了解释这句话,我们来看下面这个例子。

#题目二:异步

现有如下代码:

    console.log('A');
while (1) { }
console.log('B');
 

我们很容易想到,上方代码的打印结果是A,因为while是同步任务,代码会陷入死循环里出不来,自然也就无法打印B。可如果我把代码改成下面的样子:

    console.log('A');

    setTimeout(function () {
console.log('B');
}) while (1) { }
 

上方代码的打印结果仍然是A。因为while是同步任务,setTimeout是异步任务,所以还是那句话:如果同步任务没有执行完,队列里的异步任务是不会执行的。

#题目三:同步

    console.log('A');

    alert('haha'); //1秒之后点击确认

    console.log('B');

 

alert函数是同步任务,我只有点击了确认,才会继续打印B

#同步和异步的对比

我们在上面列举了异步和同步的例子。现在来描述一下区别:【重要】

因为setTimeout是异步任务,所以程序并不会卡在那里,而是继续向下执行(即使settimeout设置了倒计时一万秒);但是alert函数是同步任务,程序会卡在那里,如果它没有执行,后面的也不会执行(卡在那里,自然也就造成了阻塞)。

#前端使用异步的场景

什么时候需要等待,就什么时候用异步。

  • 定时任务:setTimeout(定时炸弹)、setInterval(循环执行)

  • 网络请求:ajax请求、动态<img>加载

  • 事件绑定(比如说,按钮绑定点击事件之后,用户爱点不点。我们不可能卡在按钮那里,什么都不做。所以,应该用异步)

  • ES6中的Promise

代码举例:

    console.log('start');
var img = document.createElement('img');
img.onload = function () {
console.log('loaded');
}
img.src = '/xxx.png';
console.log('end');
 

上图中,先打印start,然后执行img.src = '/xxx.png',然后打印end,最后打印loaded

#任务队列和Event Loop(事件循环)

#任务队列

所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

总结:只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。【重要】

#Event Loop

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。

在理解Event Loop时,要理解两句话:

  • 理解哪些语句会放入异步任务队列

  • 理解语句放入异步任务队列的时机

#容易答错的题目

    for (var i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}


很多人以为上面的题目,答案是0,1,2,3。其实,正确的答案是:3,3,3,3

分析:for 循环是同步任务,setTimeout是异步任务。for循环每次遍历的时候,遇到settimeout,就先暂留着,等同步任务全部执行完毕(此时,i已经等于3了),再执行异步任务。

我们把上面的题目再加一行代码。最终代码如下:

    for (var i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
console.log(i);
 

如果我们约定,用箭头表示其前后的两次输出之间有 1 秒的时间间隔,而逗号表示其前后的两次输出之间的时间间隔可以忽略,代码实际运行的结果该如何描述?可能会有两种答案:

  • A. 60% 的人会描述为:3 -> 3 -> 3 -> 3,即每个 3 之间都有 1 秒的时间间隔;

  • B. 40% 的人会描述为:3 -> 3,3,3,即第 1 个 3 直接输出,1 秒之后,连续输出 3 个 3。

循环执行过程中,几乎同时设置了 3 个定时器,这些定时器都会在 1 秒之后触发,而循环完的输出是立即执行的,显而易见,正确的描述是 B。

上面这个题目的参考链接:

面试 09-02.js运行机制:异步和单线程的更多相关文章

  1. 如何通过setTimeout理解JS运行机制详解

    setTimeout()函数:用来指定某个函数或某段代码在多少毫秒之后执行.它返回一个整数,表示定时器timer的编号,可以用来取消该定时器. 例子 ? 1 2 3 4 5 console.log(1 ...

  2. js 运行机制

    <script> console.log(1) setTimeout(function(){ console.log(3) },0) console.log(2) </script& ...

  3. Js 运行机制 (重点!!)

    一.引子 本文介绍JavaScript运行机制,这一部分比较抽象,我们先从一道面试题入手: 这一题看似很简单,但如果你不了解JavaScript运行机制,很容易就答错了.题目的答案是依次输出1 2 3 ...

  4. Js 运行机制 event loop

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

  5. js 运行机制简单了解

    一.如何理解 JS 的单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊. JavaScrip ...

  6. 从浏览器多进程到JS单线程,JS运行机制的一次系统梳理

    前言 见解有限,如有描述不当之处,请帮忙及时指出,如有错误,会及时修正. ----------超长文+多图预警,需要花费不少时间.---------- 如果看完本文后,还对进程线程傻傻分不清,不清楚浏 ...

  7. 从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理

    前言 来源:https://dailc.github.io/2018/01/21/js_singlethread_eventloop.html 见解有限,如有描述不当之处,请帮忙及时指出,如有错误,会 ...

  8. js运行机制及异步编程(一)

    相信大家在面试的过程中经常遇到查看执行顺序的问题,如setTimeout,promise,async await等等,各种组合,是不是感觉头都要晕掉了,其实这些问题最终还是考察大家对js的运行机制是否 ...

  9. js异步梳理:1.从浏览器的多进程到JS的单线程,理解JS运行机制

    大家很早就知道JS是一门单线程的语言.但是也时不时的会看到进程这个词.首先简单区分下线程和进程的概念 1. 简单理解进程 - 进程是一个工厂,工厂有它的独立资源 - 工厂之间相互独立 - 线程是工厂中 ...

随机推荐

  1. TestLink在线Excel用例转换xml

    [原文链接]:https://blog.tecchen.tech ,博文同步发布到博客园. 由于精力有限,对文章的更新可能不能及时同步,请点击上面的原文链接访问最新内容. 欢迎访问我的个人网站:htt ...

  2. 企业级工作流解决方案(十一)--集成Abp和ng-alain--权限系统服务

    权限系统主要定义为管理员增删改查权限数据,直接读取数据库,权限系统服务主要定义为供其他系统调用的权限验证接口,定义为两个不同的微服务. 权限系统有一个特点,数据变动比较小,数据量本身并不是很大,访问量 ...

  3. 深度分析:Java多线程,线程安全,并发包

    1:synchronized(保证原子性和可见性) 1.同步锁.多线程同时访问时,同一时刻只能有一个线程能够访问使synchronized修饰的代码块或方法.它修饰的对象有以下几种: 修饰一个代码块, ...

  4. property,类方法和静态方法

    # from math import pi # # class Circle: # def __init__(self, r): # self.r = r # # @property # def pe ...

  5. 用思维导图软件iMindMap来提高记忆

    虽说人的大脑是强大的存储器,但是我们终究没有挖掘出大脑全部的潜能,在记忆时或许因为方式.或许是干扰因素都能够影响我们的记忆力,致使有心无力,快来让思维导图来拯救你的记忆吧. 记忆是经验的关键,思维导图 ...

  6. 01-01.单一职责原则(Single Responsibility)

    1.基本介绍 对于类来说的,就是一个类,应该只负责一项职责(一个类只管一件事). 如类A负责两个不同职责:职责1,职责2. 当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解 ...

  7. Luogu P2656 采蘑菇

    尽管是缩点的习题,思路也是在看了题解后才明白的. 首先,每个强连通分量内的点都是一定互通的,也就是可以完全把这里面的边都跑满,摘掉所有能摘的蘑菇.那么,考虑给每一个强连通分量化为的新点一个点权,代表摘 ...

  8. mfc 位图本地存储 本地位图读取显示

    一.读取CImage //在绘图函数中直接使用参数pDC,无需定义 CDC* pDC = GetDC(): //读去位图路径,根据实际情况修改 CString loatImagePath = TEXT ...

  9. 【NOIP2017提高A组模拟9.17】组合数问题

    [NOIP2017提高A组模拟9.17]组合数问题 题目 Description 定义"组合数"S(n,m)代表将n 个不同的元素拆分成m 个非空集合的方案数. 举个例子,将{1,2,3}拆分成2 个 ...

  10. pthread 互斥量

    参考文献: [1] https://computing.llnl.gov/tutorials/pthreads/ 温故知新 在 OS 中,每个进程都独立地拥有: Process ID, process ...