前言

说到js的单线程(single threaded)和异步(asynchronous),很多同学不禁会想,这不是自相矛盾么?其实,单线程和异步确实不能同时成为一个语言的特性。js选择了成为单线程的语言,所以它本身不可能是异步的,但js的宿主环境(比如浏览器,Node)是多线程的,宿主环境通过某种方式(事件驱动,下文会讲)使得js具备了异步的属性。往下看,你会发现js的机制是多么的简单高效!

说说浏览器

js是单线程语言,浏览器只分配给js一个主线程,用来执行任务(函数),但一次只能执行一个任务,这些任务形成一个任务队列排队等候执行,但前端的某些任务是非常耗时的,比如网络请求,定时器和事件监听,如果让他们和别的任务一样,都老老实实的排队等待执行的话,执行效率会非常的低,甚至导致页面的假死。所以,浏览器为这些耗时任务开辟了另外的线程,主要包括http请求线程,浏览器定时触发器,浏览器事件触发线程,这些任务是异步的。下图说明了浏览器的主要线程。



图片来自popAnt 画得太好,忍不住引过来 (http://blog.csdn.net/kfanning/article/details/5768776)

再说说任务队列

刚才说到浏览器为网络请求这样的异步任务单独开了一个线程,那么问题来了,这些异步任务完成后,主线程怎么知道呢?答案就是回调函数,整个程序是事件驱动的,每个事件都会绑定相应的回调函数,举个栗子,有段代码设置了一个定时器

setTimeout(function(){
console.log(time is out);
},50);

执行这段代码的时候,浏览器异步执行计时操作,当50ms到了后,会触发定时事件,这个时候,就会把回调函数放到任务队列里。整个程序就是通过这样的一个个事件驱动起来的。

所以说,js是一直是单线程的,浏览器才是实现异步的那个家伙。

说回主线程

js一直在做一个工作,就是从任务队列里提取任务,放到主线程里执行。下面我们来进行更深一步的理解。



图片来自Philip Roberts的演讲《Help, I'm stuck in an event-loop》非常深刻!

我们把刚才了解的概念和图中做一个对应,上文中说到的浏览器为异步任务单独开辟的线程可以统一理解为WebAPIs,上文中说到的任务队列就是callback queue,我们所说的主线程就是有虚线组成的那一部分,堆(heap)和栈(stack)共同组成了js主线程,函数的执行就是通过进栈和出栈实现的,比如图中有一个foo()函数,主线程把它推入栈中,在执行函数体时,发现还需要执行上面的那几个函数,所以又把这几个函数推入栈中,等到函数执行完,就让函数出栈。等到stack清空时,说明一个任务已经执行完了,这时就会从callback queue中寻找下一个人任务推入栈中(这个寻找的过程,叫做event loop,因为它总是循环的查找任务队列里是否还有任务)。

借以解释几个容易困惑的问题

  1. setTimeout(f1,0)是什么鬼

    这个语句最大的疑问是,f1是不是立刻执行?答案是不一定,因为要看主线程内的命令是否已经执行完了,如下代码:
setTimeout(function(){
console.log(1);
},0);
console.log(2);

这段代码的输出结果是2,1。因为执行setTimeou后,会立即把匿名函数放到callback queue里面等待主线程的召唤,但这个时候stack里面并不是空的,因为还有一句console.log(2)。等到执行完console.log(2)后,才通过event loop把匿名函数放到stack里面。所以setTimeout(f1,0)这个语句并不是没有意义,如果f1是很耗时的任务,那就应该把任务放到callback queue里面,等到主程序执行完后再执行。

2. Ajax请求是否异步

了解完上文内容,我们就知道了,ajax请求内容的时候是异步的,当请求完成后,会触发请求完成的事件,然后把回调函数放入callback queue,等到主线程执行该回调函数时还是单线程的。

3. 界面渲染线程是单独开辟的线程,是不是DOM一变化,界面就立刻重新渲染?

如果DOM一变化,界面就立刻重新渲染,效率必然很低,所以浏览器的机制规定界面渲染线程和主线程是互斥的,主线程执行任务时,浏览器渲染线程处于挂起状态。

如何利用浏览器的异步机制

我们已经知道,js一直是单线程执行的,浏览器为几个明显的耗时任务单独开辟线程解决耗时问题,但是js除了这几个明显的耗时问题外,可能我们自己写的程序里面也会有耗时的函数,这种情况怎么处理呢?我们肯定不能自己开辟单独的线程,但我们可以利用浏览器给我们开放的这几个窗口,浏览器定时器线程和事件触发线程是好利用的,网络请求线程不适合我们使用。下面我们具体看一下:

假设耗时函数是f1,f1是f2的前置任务。

  • 利用定时器触发线程
function f1(callback){
setTimeout(function(){
// f1 的代码
callback();
},0);
}
f1(f2);

这种写法的耦合度高。

  • 利用事件触发线程
$f1.on('custom',f2);  //这里绑定事件以jQuery写法为例
function f1(){
setTimeout(function(){
// f1的代码
$f1.trigger('custom');
},0);
}

这种方法通过绑定自定义事件,对方法一解耦,这样可以通过绑定不同的事件,实现不同的回调函数,但如果应用这种方法过多,不利于阅读程序。

异步的好处和适合的场景

  1. 异步的好处

    我们直接通过一个例子对同步和异步进行对比,假设有四个任务(编号为1,2,3,4),它们的执行时间都是10ms,其中任务2是任务3的前置任务,任务2需要20ms的响应时间。下面我们做下对比,你就知道怎么实现的非阻塞I/O了。



    图片来自Soham Kaman的文章
  2. 适合的场景

    可以看出,当我们的程序需要大量I/O操作和用户请求时,js这个具备单线程,异步,事件驱动多种气质的语言是多么应景!相比于多线程语言,它不必耗费过多的系统开销,同时也不必把精力用于处理多线程管理,相比于同步执行的语言,宿主环境的异步和事件驱动机制又让它实现了非阻塞I/O,所以你应该知道它适合什么样的场景了吧!

参考内容:

http://www.sohamkamani.com/blog/2016/03/14/wrapping-your-head-around-async-programming/

https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

https://vimeo.com/96425312

http://blog.csdn.net/kfanning/article/details/5768776

http://www.ruanyifeng.com/blog/2014/10/event-loop.html

http://www.ruanyifeng.com/blog/2012/12/asynchronous_javascript.html

js的单线程和异步的更多相关文章

  1. js的单线程与异步

    一. js 是单线程和异步 1. js 是单线程的,js 的宿主环境(浏览器)是多线程的,实现异步. 2.js是单线程语言,浏览器值分配给js一个主线程,用来执行任务(函数),但一次只能执行一个任务, ...

  2. 【本周主题】第一期:JavaScript单线程与异步

    相信下边这个图一定都不陌生,本周就围绕这张图深入了解下js代码执行时的来龙去脉. 一.JavaScript是单线程的 2018-11-19 21:21:21 周一 js本质是单线程的.这一特性是jav ...

  3. JS中的同步异步问题

    <script> /* * JS 是单线程 * 同步 异步 * 常见的异步 * 1.定时器 * 2.事件绑定 * 3.ajax请求(一般的都是异步) * 4.回调函数也可以理解成 异步 * ...

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

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

  5. 【本周面试题】第2周 - js单线程和异步相关问题

    硬性知识点考察: 为什么js是单线程的? 因为js设计最初是为了操作dom而生,如果是多线程的,当多个线程同时修改一个dom时就会产生冲突,所以设计成单线程,一次只能做一件事. 既然是单线程为什么要有 ...

  6. 为什么JS是单线程?JS中的Event Loop(事件循环)?JS如何实现异步?setimeout?

    https://segmentfault.com/a/1190000012806637 https://www.jianshu.com/p/93d756db8c81 首先,请牢记2点: (1) JS是 ...

  7. JS三座大山再学习 ---- 异步和单线程

    本文已发布在西瓜君的个人博客,原文传送门 前言 写这一篇的时候,西瓜君查阅了很多资料和文章,但是相当多的文章写的都很简单,甚至互相之间有矛盾,这让我很困扰:同时也让我坚定了要写出一篇好的关于JS异步. ...

  8. JS单线程和异步

    线程和单线程的概念: 线程:是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同 ...

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

    09-02.js运行机制:异步和单线程 #前言 面试时,关于同步和异步,可能会问以下问题: 同步和异步的区别是什么?分别举一个同步和异步的例子 一个关于 setTimeout 的笔试题 前端使用异步的 ...

随机推荐

  1. Windows Server 2012 NIC Teaming介绍及注意事项

    Windows Server 2012 NIC Teaming介绍及注意事项 转载自:http://www.it165.net/os/html/201303/4799.html Windows Ser ...

  2. spark处理大规模语料库统计词汇

    最近迷上了spark,写一个专门处理语料库生成词库的项目拿来练练手, github地址:https://github.com/LiuRoy/spark_splitter.代码实现参考wordmaker ...

  3. CoreCRM 开发实录——想用国货不容易

    昨天(2016年12月29日)发了开始开发的文章.本来晚上准备在 Coding.NET 上添加几个任务开始搞起了.可是真的开始用的时候才发现:Coding.NET 的任务功能只针对私有的任务开放.我想 ...

  4. [C#] 剖析 AssemblyInfo.cs - 了解常用的特性 Attribute

    剖析 AssemblyInfo.cs - 了解常用的特性 Attribute [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5944391.html 序 ...

  5. Oracle 数据库语句大全

    Oracle数据库语句大全 ORACLE支持五种类型的完整性约束 NOT NULL (非空)--防止NULL值进入指定的列,在单列基础上定义,默认情况下,ORACLE允许在任何列中有NULL值. CH ...

  6. ASP.Net MVC——使用 ITextSharp 完美解决HTML转PDF(中文也可以)

    前言: 最近在做老师交代的一个在线写实验报告的小项目中,有这么个需求:把学生提交的实验报告(HTML形式)直接转成PDF,方便下载和打印. 以前都是直接用rdlc报表实现的,可这次牵扯到图片,并且更为 ...

  7. 图解Spark API

    初识spark,需要对其API有熟悉的了解才能方便开发上层应用.本文用图形的方式直观表达相关API的工作特点,并提供了解新的API接口使用的方法.例子代码全部使用python实现. 1. 数据源准备 ...

  8. QDEZ集训笔记【更新中】

    这是一个绝妙的比喻,如果青岛二中的台阶上每级站一只平度一中的猫,差不多站满了吧 自己的理解 [2016-12-31] [主席树] http://www.cnblogs.com/candy99/p/61 ...

  9. 利用HAProxy代理SQL Server的AlwaysOn辅助副本

    利用HAProxy代理SQL Server的AlwaysOn辅助副本 公司最近数据库升级到SQL Server2014 ,并部署了alwayson高可用集群 机房内有三套程序需要读取数据库 第一套:主 ...

  10. 用Taurus.MVC 做个企业站(下)

    前言: 上一篇完成了首页,这一篇就把剩下的几个功能给作了吧. 包括文章列表.文章详情和产品展示. 1:文章列表: 原来的ArticleList.aspx 1:现在的articlelist.html 除 ...