千古前端图文教程

vuePress-theme-reco 千古壹号    2021 - 2022
千古前端图文教程

Choose mode

  • dark
  • auto
  • light
公众号
博客
GitHub

千古壹号

0

Article

0

Tag

公众号
博客
GitHub
  • 00-前端工具

  • 01-HTML

  • 02-CSS基础

  • 03-CSS进阶

  • 04-JavaScript基础

  • 05-JavaScript基础:ES6语法

  • 06-JavaScript基础:异步编程

  • 07-JavaScript进阶

  • 08-前端基本功:CSS和DOM练习

  • 09-移动Web开发

  • 10-MySQL数据库

  • 11-Node.js

  • 12-Vue基础

  • 13-React基础

  • 14-前端性能优化

  • 15-前端面试

    • 00-准备
    • 01-页面布局
    • 02-CSS盒模型及BFC
    • 03-DOM事件的总结
    • 04-HTTP协议
    • 05-01.创建对象和原型链
    • 05-02.面向对象:类的定义和继承的几种方式
    • 06-跨域通信类
    • 07-安全问题:CSRF和XSS
    • 08-算法问题
    • 09-js运行机制:异步和单线程
    • 10-页面性能优化
    • 11-前端错误监控
  • 16-前端工程化

  • 17-前端综合

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

vuePress-theme-reco 千古壹号    2021 - 2022

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

千古壹号
(adsbygoogle = window.adsbygoogle || []).push({});

# 前言

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

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

  • 一个关于 setTimeout 的笔试题

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

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

  • 如何理解JS的单线程

  • 什么是任务队列

  • 什么是 EventLoop

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

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

# JS的异步和单线程

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

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

# 题目一:异步

现有如下代码:

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

1
2
3
4
5
6
7

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

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

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

上方代码中,打印的顺序仍然是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');
1
2
3
4
5

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

    console.log('A');

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

    while (1) {

    }

1
2
3
4
5
6
7
8
9
10

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

# 题目三:同步

    console.log('A');

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

    console.log('B');

1
2
3
4
5
6

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');
1
2
3
4
5
6
7

上图中,先打印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);
    }
1
2
3
4
5

很多人以为上面的题目,答案是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
2
3
4
5
6

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

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

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

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

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

  • 80% 应聘者都不及格的 JS 面试题

  • 深入浅出Javascript事件循环机制(上)