Javascript 单线程指的是在一个浏览器进程中只存在一个 Javascript 执行线程,所以任务需要顺序排列等待执行,而不能像 Java 等多线程语言一样并发执行。但是这种单线程模型在处理耗时的异步任务是会出现较长时间的线程阻塞,导致后续的任务不能被及时处理。所以在 Javascript 中存在异步的处理方式用于处理这种情况,不过严格来说所谓的异步,本质上还是借助于多线程的宿主实现的,并发 Javascript 语言本身特性。我想尝试着总结一下在不同的宿主环境下,Javascript 的异步实现机制。

但凡“ 即是单线程又是异步 ”的语言都有一个共同的特点:它们是 event-driven 的,所以 Javascript 异步的实现也与其事件机制关系密切。

在浏览器端:

浏览器端的 Javascript 实现了两个很重要的异步 API,它们分别是 定时器AJAX请求,它们具体都是怎么工作的呢?

定时器

定时器比如 setTimeout 被执行时,由浏览器的定时器线程执行的定时计数,而并不是 Javascript 执行线程负责计数,可以想象如果是 Javascript 执行线程负责计数,那必定会造成执行线程的阻塞。定时器线程在定时时间将定时事件推入 Javascript 事件队列。当 Javascript 主线程同步代码执行完毕时,会去轮询该事件队列,取出最开始事件的处理函数推入主线程中被执行。

解释至此我们可以知道,为什么会说 Javascript 的定时器是不完全准时触发的呢?因为 Javascript 事件队列中的任务是被顺序取出执行的,如果在定时任务之前还存在其它的任务,或者主线程中的同步任务还没有被执行完毕,则定时任务会等到这之前的任务全部执行完毕之后,即主线程空闲出来了,才会被取出执行。

一个关于定时器的例子

我们希望在 500 ms 之后触发定时事件,然而上面这一段代码在 chrome 中执行的结果定时事件却是在 1000 ms 后才被触发。因为我们在定时器的下面写了一个空循环,在还不到 1000 ms 时 Javascript 主线程不会处于空闲状态,主线程同步代码在还没有执行完毕时,Javascript 不会去取出事件队列中的回调执行。

AJAX

AJAX 请求和定时器类似,同样是委托浏览器线程代为执行耗时任务,这里是借由浏览器的HTTP请求线程发起对服务器的请求,在请求得到响应之后触发请求完成事件,将回调函数推入事件队列等待执行。

req.send()方法是 AJAX  向服务器发生数据,它是一个异步任务,而 req.onreadystatechange()属于事件回调,只有在主线程同步代码执行完毕之后才会被从事件队列中取出执行,所以它是在 req.send()方法前面还是后面无关紧要,因为不管处于哪个位置,它都不会被立即执行。

这让我们想起似乎我们在给 DOM 元素绑定交互事件的时候也是这样,我们不需要去关心在文件的哪个区域声明我们的事件监听函数。其实原理是类似的,当用户点击一个绑定点击处理函数的 DOM 元素时,会有一个点击事件排入事件队列,该点击事件也需要等到当前所有正在运行的代码结束之后(可能还要等待其它此前已排队的事件也依次结束),才会执行。

NodeJS端:

NodeJS 的异步实现和浏览器端实现有所不同。在 NodeJS 中 Libuv 为 Node.js 提供了跨平台,线程池,事件池,异步 I/O 等能力,是 Node.js 如此强大的关键。Libuv 为上层的 Node.js 提供了统一的 API 调用,使其不用考虑平台差距,隐藏了底层实现。Libuv 本身就是异步和事件驱动的,所以,当我们将 I/O 操作的请求传达给 Libuv 之后,Libuv 开启线程来执行这次 I/O 调用,并在执行完成后,传回给 Javascript 进行后续处理。

总结来说,一个异步 I/O 的大致实现流程如下:

发起 I/O 调用

1 用户通过 Javascript 代码调用 NodeJS 核心模块,将参数和回调传入核心模块

2 NodeJS 核心模块将传入参数和回调封装为一个请求对象

3 将这个请求对象推入到I/O线程池中等待执行

4 Javascript 发起的异步调用结束,Javascript 线程继续执行后续操作

执行回调

1 异步任务完成之后,会将结果存放在请求对象的 result 属性上,并发出操作完成通知

2 每次事件循环时会检查 I/O 线程池中是否存在已经完成的 I/O 操作,如果有就将请求事件加入到I/O观察者队列当中,之后当作事件处理

3 处理I/O观察者事件时,会将之前封装在请求对象中的回调函数取出,并将 result 参数传入执行,以完成 Javascript 回调的目的

完结

参考:

Node.JS探秘 初始单线程的Node.JS

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

深入检出 NODEJS

 

Javascript 异步实现机制的更多相关文章

  1. JavaScript异步机制

    单线程异步执行的JavaScript JavaScript是单线程异步执行的,单线程意味着代码在任务队列中会按照顺序一个接一个的执行.异步代表JavaScript代码在任务队列中的顺序并不完全等同于代 ...

  2. JavaScript的异步运行机制

    ----异步运行机制如下: 1.左右同步任务都在主线程上执行,形成一个执行栈 2.主线程值外,还存在一个任务队列,只要异步任务有了运行结果,就在任务队列中放置一个事件 3.一旦执行栈中的所有同步任务执 ...

  3. [转]JavaScript异步机制详解

    原文: https://www.jianshu.com/p/4ea4ee713ead --------------------------------------------------------- ...

  4. 前端知识点回顾之重点篇——JavaScript异步机制

    JavaScript异步机制 来源:https://www.cnblogs.com/zhaodongyu/p/3922961.html JavaScript是单线程异步执行的,单线程意味着代码在任务队 ...

  5. javaScript的执行机制-同步任务-异步任务-微任务-宏任务

    一.概念理解 1.关于javascript javascript是一门单线程语言,在最新的HTML5中提出了Web-Worker,但javascript是单线程这一核心仍未改变.所以一切javascr ...

  6. JavaScript异步编程的主要解决方案—对不起,我和你不在同一个频率上

    众所周知(这也忒夸张了吧?),Javascript通过事件驱动机制,在单线程模型下,以异步的形式来实现非阻塞的IO操作.这种模式使得JavaScript在处理事务时非常高效,但这带来了很多问题,比如异 ...

  7. JavaScript异步编程原理

    众所周知,JavaScript 的执行环境是单线程的,所谓的单线程就是一次只能完成一个任务,其任务的调度方式就是排队,这就和火车站洗手间门口的等待一样,前面的那个人没有搞定,你就只能站在后面排队等着. ...

  8. JavaScript异步编程(1)- ECMAScript 6的Promise对象

    JavaScript的Callback机制深入人心.而ECMAScript的世界同样充斥的各种异步操作(异步IO.setTimeout等).异步和Callback的搭载很容易就衍生"回调金字 ...

  9. 简述JavaScript的运行机制

    想要理解JavaScript的运行机制,需要分别深刻理解以下几个点: · JavaScript的单线程机制 · 任务队列(同步任务和异步任务) · 事件和回调函数 · 定时器 · Event Loop ...

随机推荐

  1. Bootstrap兼容处理

    接将一下代码引用到页面 </body> 之前 <script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js" ...

  2. STM8时钟系统详解

    就我个人看来,研究一块单片机,分为新手和老手两种模式,新人迫切的想先用,你得告诉他们怎么样最快的写出一个能跑起来的程序,告诉他们每一个外设的使用方式,老手不同,用的单片机多了外设对于他们而言没太多好奇 ...

  3. python实现断点续传下载文件

    最近的任务里有一个功能是要我从日志服务器实时跟新日志到本地,日志在不断新增内容就需要我隔一段时间从上次下载的位置继续下载,并写入本地文件上次写完的位置后面. headers = {'Range': ' ...

  4. 家用计费系统ER图

    家用计费系统研发开始.在此记录自己的开发过程.

  5. Java的三种代理模式

    Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩 ...

  6. PHP 合并数组 追加数组例子

    PHP合并数组我们可以使用array_merge()函数,array_merge()函数返回一个联合的数组.所得到的数组以第一个输入数组参数开始,按后面数组参数出现的顺序依次追加.其形式为: arra ...

  7. redis php sort 函数

    很多人把redis当成一种数据库,其实是利用redis来构造数据库的模型,有那种数据库的味道.但是在怎么构建还是key和value的关系.根真正的关系型数据库还是不一样的.效率高,不方便:方便的,效率 ...

  8. 必备的实用jQuery代码段(1)

    1. 如何正确地使用toggleClass: //切换(toggle)类允许你根据某个类的 //是否存在来添加或是删除该类. //这种情况下有些开发者使用: a.hasClass('blueButto ...

  9. js原生之设计模式开篇介绍

    本文主要讲述一下,什么是设计模式(Design pattern),作为敲键盘的我们要如何学习设计模式.设计模式真的是一把万能钥匙么?     各个代码的设计模式几乎每个人都知晓,就算不会那也一定在一些 ...

  10. PKU-1704-Georgia and Bob

    题目链接 http://poj.org/problem?id=1704 这个题目是个好题,没有两下子是做不出的,其中考到,要你排序,如何把题目化成我们熟知的东西, 在这个题中我开始用选择法排序,他给我 ...