本博文基于知乎"JavaScript作用域问题?"一问,而引起了对JavaScript事件循环和单线程等概念与实践上的研究、深入理解。

一、概念
  0.关键词:JavaScript单线程事件循环(event loop)事件队列(event queue)执行栈(execution context stack)
  

  1.JavaScript引擎属于单线程作业所谓单线程,是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个,也不妨叫它主线程。JavaScript引擎属于单线程作业,意味着:在同一时间只能执行一个代码块,这些代码块的执行就阻塞了异步事件的处理。[From JavaScript忍者秘籍]
  JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
    1.1 单线程意味着,【所有任务】都需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
    1.2 如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
    1.3 JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。
    1.4 于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
    1.5 同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
    1.6 异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
    1.7 具体来说,异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)
      (1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
      (2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
      (3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
      (4)主线程不断重复上面的第三步。
    1.8 只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。
      [From 阮一峰老师:http://www.ruanyifeng.com/blog/2014/10/event-loop.html]

  2.事件循环(用于解决:异步问题/异步事件):在初期许多人会把异步理解成类似多线程的编程模式,其实他们中有着很大的差别,要完全理解异步,就需要了解 JS 的运行核心——事件循环(event loop)。
    2.1 事件循环:【事件队列】是一个存储着待执行任务的队列,其中的任务严格按照时间先后顺序执行,排在队头的任务将会率先执行,而排在队尾的任务会最后执行。事件队列每次仅执行一个任务,在该任务执行完毕之后,再执行下一个任务。【执行栈】则是一个类似于函数调用栈的运行容器,当执行栈为空时,JavaScript引擎便检查事件队列,如果不为空的话,事件队列便将第一个任务压入中运行。
  [From http://www.php.cn/js-tutorial-369771.html]
    2.2 常见异步任务:定时器任务(setTimeout();setInterval();)、Ajax事件浏览器/用户行为事件(例如:浏览器加载(load)、鼠标单击click、鼠标滑动/滑过/离开(mouseover、mouseout、mouseleave等)

二、分析

  由于JavaScript是单线程作业,当一个异步事件发生时(比如:鼠标单击、定时器触发甚至是XMLHttpRequest的完成事件),它就会排队,并且在线程空闲时才进行执行。且实际上,每个浏览器的排队机制是不同的。当我们设置一个延迟函数的时候,当前脚本并不会阻塞,它只是会在浏览器的事件表中进行记录,程序会继续向下执行。当延迟的时间结束之后,事件表会将回调函数添加至事件队列(task queue)中,事件队列拿到了任务过后便将任务压入执行栈(stack)当中,执行栈执行任务,执行console.log("after 1000 mills:",i);

for(var i = 0; i < 10; i++) {
console.log("cur:",i);
setTimeout(function() {
console.log("after 1000 mills:",i); //当 console.log 被调用的时候,匿名函数保持对外部变量 i 的引用,此时for循环已经结束, i 的值被修改成了 10.
}, 1000);
}

三、解决方案

  即时函数

    格式:(function(){ //...statement })(); 

for(var i = 0; i < 10; i++) {
console.log("cur:",i),
(function(i){
setTimeout(function() {
console.log("after 1000 mills:",i);
}, 1000);
})(i);//通过即时函数(1.创建函数实例,2.执行该函数,3.销毁该函数),将循环体异步事件压入执行栈中,立即执行的特性,以维护好变量当前的值
}

 

四、引用文献

  《JavaScript忍者秘籍》

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

  为什么会有异步? 什么是事件队列?

JavaScript之JS单线程|事件循环|事件队列|执行栈的更多相关文章

  1. Javascript并发模型和事件循环

    Javascript并发模型和事件循环 JavaScript的"并发模型"是基于事件循环的,这个并发模型有别于Java的多线程, javascript的并发是单线程的. Javas ...

  2. javascript的event loop事件循环

    javascript的event loop事件循环 这是今天一个朋友发给我的一个面试题, 感觉还挺有意思的, 写个博客以供分享 先看看这个面试题目: 观察下面的代码,写出输出结果 console.lo ...

  3. Node.js:事件循环

    ylbtech-Node.js:事件循环 1.返回顶部 1. Node.js 事件循环 Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高. Node.js 的每一个 ...

  4. 浏览器中 JS 的事件循环机制

    目录 事件循环机制 宏任务与微任务 实例分析 参考 1.事件循环机制 浏览器执行JS代码大致可以分为三个步骤,而这三个步骤的往复构成了JS的事件循环机制(如图). 第一步:主线程(JS引擎线程)中执行 ...

  5. js的事件循环绑定和jQuery的隐式迭代

    js的事件循环绑定和jQuery的隐式迭代 js事件循环绑定 jQuery隐式迭代 先举一个例子:给定一个ul,点击列表内的每一个li元素,使它的背景色变红,下边分别用js代码和jQuery实现. & ...

  6. Node.js 的事件循环机制

    目录 微任务 事件循环机制 setImmediate.setTimeout/setInterval 和 process.nextTick 执行时机对比 实例分析 参考 1.微任务 在谈论Node的事件 ...

  7. js的事件循环机制:同步与异步任务(setTimeout,setInterval)宏任务,微任务(Promise,process.nextTick)

    javascript是单线程,一切javascript版的"多线程"都是用单线程模拟出来的,通过事件循环(event loop)实现的异步. javascript事件循环 事件循环 ...

  8. JS基础-事件循环机制

    从一道题浅说 JavaScript 的事件循环 原文链接: https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/7 ...

  9. JavaScript 如何工作的: 事件循环和异步编程的崛起 + 5 个关于如何使用 async/await 编写更好的技巧

    原文地址:How JavaScript works: Event loop and the rise of Async programming + 5 ways to better coding wi ...

随机推荐

  1. Test Scenarios for result grid

    1 Page loading symbol should be displayed when it is taking more than default time to load the resul ...

  2. Square Root

    Square RootWhen the square root functional configuration is selected, a simplified CORDIC algorithm ...

  3. anaconda2安装cv2

    http://m.blog.csdn.net/u010167269/article/details/62447648 下载离线安装包:https://anaconda.org/menpo/opencv ...

  4. codeforces625C

    K-special Tables CodeForces - 625C 人们经常做一些疯狂的事来凸显自己.有的人跳舞,有的人撩妹,有的人立志成为顶级程序猿(例如某peng),还有的人喜欢收集有趣的数学对 ...

  5. 优步加入Linux基金会:支持开源

    导读 当地时间11月16日,优步在Uber Open Summit 2018年度峰会上宣布加入Linux基金会,并作为金级会员坚定支持对开源工具的使用和贡献. 优步CEO Thuan Pham将Lin ...

  6. BZOJ2277[Poi2011]Strongbox——数论

    题目描述 Byteasar is a famous safe-cracker, who renounced his criminal activity and got into testing and ...

  7. Django实现websocket完成实时通讯,聊天室,在线客服等

    一 什么是Websocket WebSocket是一种在单个TCP连接上进行全双工通信的协议 WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据.在WebS ...

  8. 【比赛】NOIP2018 总结

    一.考试过程 Day1: 先看了一遍题目,得到的结论是没有题是直接秒掉的,然后一道一道认真看. 看T1的时候开始并没想起来有一道原题,只是脑海中有一个印象,好像求差分和可以.然后自测了一下小样例,发现 ...

  9. 自学工业控制网络之路1.3-典型的现场总线介绍FF

    返回 自学工业控制网络之路 自学工业控制网络之路1.3-典型的现场总线介绍FF 1994年6月,ISP和WORLDFIP合并成立现场总线基金会FF.1998年,FF基金会开发了HSE 10/100Mb ...

  10. .net连接ORACLE数据库

    这段时间维护客户的一个系统,该系统使用的是ORACLE数据库,之前开发的时候用的都是MSSQL,并没有使用过ORACLE.这两种数据库虽然都是关系型数据库,但是具体的操作大有不同,这里作下记录. 连接 ...