前段时间,老板交给了我一个任务:通过setTimeout来延后网站某些复杂资源的请求。正好借此机会,将JavaScript运行机制和setTimeout重新认真思考一遍,并将我对它们的理解整理如下。

1.JavaScript运行机制

核心特征:单线程

JavaScript在浏览器中是单线程运行的。所谓单线程,就是同一时刻只执行一个任务,简单来讲,就是任何时刻JS主线程都只有一句JavaScript代码在运行。

那么为什么JavaScript一定要是单线程呢?如果同时有多个线程运行岂不更强大吗?关于这个问题,在JavaScript运行机制详解一文中有给出一个通俗易懂的解释:“假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?” 所以,与用户交互和操作DOM这些主要用途,这决定了JavaScript必须是单线程的

任务队列

虽然JavaScript每个时刻只能做一个任务,但它并不是只有一个任务需要做。比如它有Ajax回调函数需要执行,它有用于监听click事件的监听函数需要执行,它有setTimeout的函数需要执行...这些在某个时机需要执行的东西,都是JavaScript需要完成的任务;JavaScript主线程当前正在执行的东西,也是它需要完成的一个任务。面对这么多任务,JavaScript一次又只能处理一个任务,用于管理任务执行的任务队列就应运而生了。

所有这些任务中首要的当然是那些被主线程顺序执行的一行一行的代码,即同步任务。主线程在执行同步任务的过程中,可能会接到未来需要执行某个其他任务的指示。那些未来需要执行的任务,即为异步任务。主线程由于手中还没忙完同步任务,就只能先把接到的异步任务的指示记录下来。

如何记录呢?那就是在合适的时候把相关异步任务推到任务队列中。这里有两点需要理解清楚。第一,并不是马上就把异步任务推到任务队列中,而是在恰当的时机再把异步任务推到队列中,这个时机比如Ajax请求成功返回的时候、click事件触发的时候、setTimeout设置的时间点到了的时候。第二,推到任务队列这个“推到”的动作并不意味着“马上执行”,它只是告诉主线程有这个任务在这里需要被执行,如果主线程有空就赶紧执行,如果主线程现在没有空,那么一旦有空就要考虑执行。

那么对于这个需要执行的任务而言,主线程什么时候才能称得上是有空了呢?第一,主线程已经执行完了同步任务;第二,主线程已经把异步队列中排在该任务之前的任务都执行完了。这时,主线程就可以放心大胆地执行该任务了。当然,该异步任务一旦被执行,对于主线程而言也就不再是异步而是同步的了。

一次正确的Event Loop执行顺序:

其实上述任务队列中的任务叫做宏任务(macrotask),又叫task。同步代码也是一种宏任务。

除了宏任务之外,还有一种微任务(microtask),又叫job。

  • 宏任务包括:script(包括同步js代码)、setTimeout、 setInternal、setImmeidate(非标准)、I/O、UI rendering
  • 微任务包括: promise等

一次正确的Event Loop执行顺序:

  • (1)执行同步代码,这属于宏任务
  • (2)执行栈为空(由执行环境,也叫执行上下文组成的),查询是否有微任务需要执行
  • (3)执行所有微任务
  • (4)必要的话渲染UI
  • (5)开始下一轮Event Loop,执行下一个宏任务(task队列里的下一段异步代码)

2. setTimeout

在理解了JavaScript运行机制后,理解setTimeout就很容易了。

JavaScript主线程在遇到代码setTimeout(cb, n)时,要做的事情为:在延迟指定时间n后,将setTimeout回调函数cb加入任务队列中

注意,经过时间n后,cb只是被加入任务队列中,而非被执行。

结合setTimeout,我们可以更清晰地理解JavaScript运行机制:

假设JavaScript主线程会先后遇到两句setTimeout代码,分别记为代码片setTimeout1和setTimeout2,其回调函数分别为cb1和cb2,延迟时间分别为delay1和delay2。则可以得到如所示的JavaScript任务执行情况图:

情况1:两个异步任务都在同步任务结束一段时间后才加入异步队列,且后一个异步任务加入时前一个异步任务已执行结束

这种情况最清晰明朗,几个任务互相没有干扰,就是说没有出现某个任务需要等待另一个任务执行结束才能执行的情况。

情况2:两个异步任务都在同步任务结束一段时间后才加入异步队列,但前一个异步任务的执行推迟了后一个异步任务的执行

在该情况下,cb2加入到异步任务队列的时候并不能马上执行,因为JS主线程还在执行cb1。待cb1执行结束后,cb2开始执行。

情况3:在同步任务还未执行结束时,已经有异步任务加入异步队列

如图所示,在同步任务还未执行完时,setTimeout1指定的delay1已经到时间了,将cb1加入异步任务队列。此时因为同步任务还未结束,所以cb1的执行会推迟到同步任务执行结束。

为了逐步增加所述情况的复杂程度,这里暂时将cb2加入异步队列的时间假定为在同步任务和cb1都已经执行完之后。

更复杂的情况

基于以上3种情况,不难得出更复杂的几种情况:

多个异步任务都是在同步任务还未执行结束时就已经加入异步队列:

有异步任务在同步任务还未结束时加入异步队列,也有异步任务在前一个异步任务还未结束时加入异步队列:

当然,还有情况是,后一个任务的加入时间早于前一个任务(比如delay1远大于delay2):

参考资料

setTimeout的那些事:
http://imweb.io/topic/56ac67fbe39ca21162ae6c78

JavaScript运行机制详解:
http://www.ruanyifeng.com/blog/2014/10/event-loop.html

JavaScript运行机制与setTimeout的更多相关文章

  1. 从setTimeout谈JavaScript运行机制

    从setTimeout说起 众所周知,JavaScript是单线程的编程,什么是单线程,就是说同一时间JavaScript只能执行一段代码,如果这段代码要执行很长时间,那么之后的代码只能尽情地等待它执 ...

  2. 深入理解JavaScript运行机制

    深入理解JavaScript运行机制 前言 本文是写作在给团队新人培训之际,所以其实本文的受众是对JavaScript的运行机制不了解或了解起来有困难的小伙伴.也就是说,其实真正的原理和本文阐述的并不 ...

  3. JavaScript运行机制详解

    JavaScript运行机制详解   var test = function(){ alert("test"); } var test2 = function(){ alert(& ...

  4. 深入浅出JavaScript运行机制

    一.引子 本文介绍JavaScript运行机制,这一部分比较抽象,我们先从一道面试题入手: console.log(1); setTimeout(function(){ console.log(3); ...

  5. javascript 运行机制 事件循环 浏览器缓存 (慕课网 前段跳槽面试必备 4-1,4-2,4-3)

    4-1 渲染机制:-1-,什么是DOCTYPE及其作用?DTD(document type definition,文档类型定义)是一系列的语法规则,用来定义XML或(X)HTML的文件类型,浏览器会使 ...

  6. Javascript 运行机制

    先看一下下面这段js代码: console.log('1'); setTimeout(function(){ console.log('2'); },0); console.log('3'); 请问打 ...

  7. JavaScript 运行机制 & EventLoop

    JavaScript 运行机制 & EventLoop 看阮老师博客和自己的理解,记录的学习笔记,js的单线程和 事件EventLoop 机制. 1. JavaScript是单线程 JavaS ...

  8. javascript运行机制

    太久没更新博客了,Javascript运行机制 Record it 1.代码块 JavaScript中的代码块是指由<script>标签分割的代码段.例如: <script type ...

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

    原文地址:http://www.ruanyifeng.com/blog/2014/10/event-loop.html 一年前,我写了一篇<什么是 Event Loop?>,谈了我对Eve ...

随机推荐

  1. What I learned from competing against a ConvNet on ImageNet

    http://karpathy.github.io/2014/09/02/what-i-learned-from-competing-against-a-convnet-on-imagenet/

  2. 【python】-- 信号量(Semaphore)、event(红绿灯例子)

    信号量(Semaphore) 之前讲的线程锁(互斥锁) 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里 ...

  3. Python菜鸟之路:Python基础

    一.Python版本升级至3.0的必然性 In November 2014, it was announced that Python 2.7 would be supported until 202 ...

  4. Linux项目部署发布

    Linux项目部署发布 1.部署环境准备,准备python3和虚拟环境解释器,virtualenvwrapper pip3 install -i https://pypi.douban.com/sim ...

  5. You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode.

    [root@localhost dreamstart]# composer installLoading composer repositories with package informationI ...

  6. 让Editplus支持sql语法高亮显示

    版权声明:作者:jiankunking 出处:http://blog.csdn.net/jiankunking 本文版权归作者和CSDN共同拥有,欢迎转载.但未经作者允许必须保留此段声明,且在文章页面 ...

  7. jauery table

    $("#tableData tr:gt(0)").each(function() { }//橘色部分是查找id为tableData的DataTable里面除第一行以外的行

  8. (转)ubuntu 12.04搭建Adobe Flash Media Server服务

    破解版传送门:http://fms45.cuplayer.com/fms4download.html 福利:1462-5247-1705-7678-8379-5590 下载解压 cd进目录,./ins ...

  9. python 3 面向过程编程

    python 3 面向过程编程 核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就像设计好一条工业流水线,是一种机械式的思维方式. 1.优点:程序结构清晰,可以把复杂的问题简单化,流程 ...

  10. 【FLASK模板】set,with语句

    # set with 语句 ###set语句:在模板中, 可以使用 ‘set’语句来定义变量, 实例如下: <body> {% set username='zhiliaoketang' % ...