深入理解 JavaScript 异步系列(1)—— 什么是异步
前言
2014年秋季写完了《深入理解javascript原型和闭包系列》,已经帮助过很多人走出了 js 原型、作用域、闭包的困惑,至今仍能经常受到好评的留言。
很早之前我就总结了JS三座大山这个概念(虽然没有到处宣扬),前两座(原型、作用域)已经基本讲明白,而第三座(异步)也应该做一个总结。
于是,2017年初春,我花费大约一周的业余时间来对 JS 异步做一个完整的总结,和各位同学共勉共进步!
原文地址:http://www.cnblogs.com/wangfupeng1988/p/6513070.html 未经同意禁止转载!
第一部分,什么是异步
提醒:如果你是初学 js 的同学,尚未有太多项目经验和基础知识,请就此打住,不要看这篇教程
我思考问题、写文章一般都不按讨论出牌,别人写过的东西我不会再照着抄一遍。因此,后面所有的内容,都是我看了许多资料之后,个人重新思考提炼总结出来的,这肯定不能算是初级教程。
如果你是已有 js 开发经验,并了解异步的基础知识,到这里来想深入了解一下Promise Generator和async-awati,那就太好了,非常欢迎。
本节内容概述
- JS 为何会有异步
- 异步的实现原理是什么
- 常用的异步操作有哪些
JS 为何会有异步
首先记住一句话 —— JS 是单线程的语言,所谓“单线程”就是一根筋,对于拿到的程序,一行一行的执行,上面的执行为完成,就傻傻的等着。例如
var i, t = Date.now()
for (i = 0; i < 100000000; i++) {
}
console.log(Date.now() - t) // 250 (chrome浏览器)
上面的程序花费 250ms 的时间执行完成,执行过程中就会有卡顿,其他的事儿就先撂一边不管了。
执行程序这样没有问题,但是对于 JS 最初使用的环境 ———— 浏览器客户端 ———— 就不一样了。因此在浏览器端运行的 js ,可能会有大量的网络请求,而一个网络资源啥时候返回,这个时间是不可预估的。这种情况也要傻傻的等着、卡顿着、啥都不做吗?———— 那肯定不行。
因此,JS 对于这种场景就设计了异步 ———— 即,发起一个网络请求,就先不管这边了,先干其他事儿,网络请求啥时候返回结果,到时候再说。这样就能保证一个网页的流程运行。
异步的实现原理
先看一段比较常见的代码
var ajax = $.ajax({
url: '/data/data1.json',
success: function () {
console.log('success')
}
})
上面代码中$.ajax()需要传入两个参数进去,url和success,其中url是请求的路由,success是一个函数。这个函数传递过去不会立即执行,而是等着请求成功之后才能执行。对于这种传递过去不执行,等出来结果之后再执行的函数,叫做callback,即回调函数
再看一段更加能说明回调函数的 nodejs 代码。和上面代码基本一样,唯一区别就是:上面代码时网络请求,而下面代码时 IO 操作。
var fs = require('fs')
fs.readFile('data1.json', (err, data) => {
console.log(data.toString())
})
从上面两个 demo 看来,实现异步的最核心原理,就是将callback作为参数传递给异步执行函数,当有结果返回之后再触发 callback执行,就是如此简单!
常用的异步操作
开发中比较常用的异步操作有:
- 网络请求,如
ajaxhttp.get - IO 操作,如
readFilereaddir - 定时函数,如
setTimeoutsetInterval
最后,请思考,事件绑定是不是也是异步操作?例如$btn.on('click', function() {...})。这个问题很有意思,我会再后面的章节经过分析之后给出答案,各位先自己想一下。
第二部分,异步和 event-loop
提到异步,就必须提 event-loop 。event-loop 中文翻译叫做“事件轮询”,它是能体现出单线程中异步操作是如何被执行的。
首先,强烈大家观看一个歪果仁的视频《what the hack is event loop》,只有不到半个小时的时间,但是将的非常详细。如果那个链接失效,访问这里(密码: xx9f)
其次,再结合阮一峰老师的《什么是event loop》一起看一下。将这两个看完就基本了解 event loop 了
最后,event-loop 是一块内容比较独立的技术性知识,它是什么样子就是什么样子,讲解起来可变通性非常小。因此,本节说一下我对 event-loop 的理解和体会
本节内容概述
- 举例说明
- 核心概念
- 思考两个问题
举例说明
给出一段简单的 js 代码,并用比较通俗、简单的说法介绍一下执行过程。详细过程还需各位去看视频,因为我没必要把半小时的视频都写到这里。
console.log('line 1')
setTimeout(console.log, 1000, 'line 2')
console.log('line 3')
以上一共三行代码,该程序被执行的时候,会依次挨行执行
- 第一步,执行第一行,将结果
line 1打印出来 - 第二步,执行第二行,注意此时会将这个操作暂时存储到其他地方,因为
setTimeout是一个异步执行操作。 - 第三步,执行第三行,将结果
line 3打印出出来 - 第四步,等待最后一行程序(一共三行)都全部执行完了,然后立马实时查看刚才暂存的异步操作有没有。如果有可执行的,就立即拿到出来继续执行。
- 第五步,执行完毕之后,再实时查看暂存位置中是否还有未执行的异步回调。
以上只拿了setTimeout举例子,但是对于网络请求、IO操作、事件绑定道理都是一样的。如果我讲的简单例子你还是看不懂,一定要去看文章最初提到的《what the hack is event loop》视频,重要重要!!!
思考三个问题
第一题,以下代码的输出顺序是什么
setTimeout(console.log, 0, 'a')
console.log('b')
console.log('c')
答案是b c a,有疑问的需要再去看上面的介绍或者那个视频。
第二题,以下代码中,最后输出的结果是否是 500
var i, t = Date.now()
for (i = 0; i < 100000000; i++) {
}
function fn() {
console.log(Date.now() - t) // 输出多少???
}
setTimeout(fn, 500)
答案是大于 500ms ,因为 for 函数需要花费一些时间,等 for 执行完之后再开始计算 500ms 之后执行 fn
第三题,事件绑定是不是异步操作?
这个问题大家根据 event-loop 的讲解和视频来思考,我们下一节再给出解答。
第三部分,事件绑定算不算异步?
如果你认真看了上一节的 event-loop 的,你会发现原来事件绑定和异步操作的实现机制是一样的,那么事件绑定是不是就是异步操作呢?(声明一下,这里说的事件绑定是如下代码的形式)
$btn.on('click', function (e) {
console.log('你点击了按钮')
})
PS:这个问题貌似没有加过有人讨论或者发起讨论,但是当我了解了 event-loop 之后,我就发现这两者有很大联系,很早就像讨论一下这个话题。不知道哪位同仁跟我有一样的想法?
本节内容概述
- 共同之处
- 不同之处
- 我的观点
共同之处
从技术实现以及书写方法上来讲,他们是一样的。例如事件绑定和 IO 操作的写法基本相同
$btn.on('click', function (e) {
console.log('你点击了按钮')
})
fs.readFile('data1.json', function (err, data) {
// 获取数据
})
最终执行的方式也基本一样,都通过 evet-loop 执行。
不同之处
在我看来至少有两处不同。
第一,event-loop 执行时,调用的源不一样。异步操作是系统自动调用,无论是setTimeout时间到了还是$.ajax请求返回了,系统会自动调用。而事件绑定就需要用户手动触发
第二,从设计上来将,事件绑定有着明显的“订阅-发布”的设计模式,而异步操作却没有。
我的观点
我个人看代码比较偏重设计,一个东西是什么要看它是未什么而设计的。因此,我倾向于事件绑定不是异步操作。虽然它也是通过 event-loop 实现调用的,但是它的设计目录却和异步操作完全不一样。
其实,事件绑定在 js 中扮演着非常重要的角色,各个地方都会用到事件绑定的形式。例如 web 页面监控鼠标、键盘,以及 nodejs 中的 EventEmitter 应用非常广泛(特别是涉及到数据流时)。而事件绑定被应用到非常广泛,却没有发生像异步操作带来的程序逻辑问题,反而大家用的非常开心————这又一个两者不一样的例证。
如果你觉得我的观点有问题,也可以大胆提出自己的建议和意见,发表出来!说对说错都无所谓,也不会扣你落户积分,只要能自圆其说就是好的。
求打赏
如果你看完了,感觉还不错,欢迎给我打赏 ———— 以激励我更多输出优质内容

最后,github地址是 https://github.com/wangfupeng1988/js-async-tutorial 欢迎 star 和 pr
-------
学习作者教程:《前端JS高级面试》《前端JS基础面试题》《React.js模拟大众点评webapp》《zepto设计与源码分析》《json2.js源码解读》
深入理解 JavaScript 异步系列(1)—— 什么是异步的更多相关文章
- 深入理解javascript函数系列第一篇——函数概述
× 目录 [1]定义 [2]返回值 [3]调用 前面的话 函数对任何一门语言来说都是一个核心的概念.通过函数可以封装任意多条语句,而且可以在任何地方.任何时候调用执行.在javascript里,函数即 ...
- 深入理解javascript函数系列第二篇——函数参数
× 目录 [1]arguments [2]内部属性 [3]函数重载[4]参数传递 前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传 ...
- 深入理解javascript作用域系列第一篇——内部原理
× 目录 [1]编译 [2]执行 [3]查询[4]嵌套[5]异常[6]原理 前面的话 javascript拥有一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域.作用域 ...
- 深入理解javascript作用域系列第二篇——词法作用域和动态作用域
× 目录 [1]词法 [2]动态 前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极 ...
- 深入理解javascript函数系列第三篇——属性和方法
× 目录 [1]属性 [2]方法 前面的话 函数是javascript中的特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本 ...
- 深入理解javascript作用域系列第四篇——块作用域
× 目录 [1]let [2]const [3]try 前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用 ...
- 深入理解javascript作用域系列第三篇——声明提升(hoisting)
× 目录 [1]变量 [2]函数 [3]优先 前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javasc ...
- 深入理解javascript作用域系列第三篇
前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javascript作用域系列第三篇——声明提升(hois ...
- 深入理解javascript作用域系列第二篇
前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极易出错.这实际上是由两种作用域工作 ...
- 深入理解javascript作用域系列第一篇
前面的话 javascript拥有一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域.作用域貌似简单,实则复杂,由于作用域与this机制非常容易混淆,使得理解作用域的原 ...
随机推荐
- Jenkins搭建Windows slave 环境
因为之前的接口测试平台是在windows上,后来换了linux,花了半天时间也没有成功,就想着建立一个windows的节点曲线救国 创建WindowsSlave 首先要保证windows和sl ...
- VS2013 F12无法转到函数的定义处,总是从“元数据”获取的问题 ——解决方法
右键项目名称-->点击菜单中的"添加"-->点击"引用"-->在弹出窗中点击"解决方案"下的"项目", ...
- linux 同步机制之complete【转】
转自: http://blog.csdn.net/wealoong/article/details/8490654 在Linux内核中,completion是一种简单的同步机制,标志"thi ...
- 启动子&外显子&内含子
启动子 http://baike.baidu.com/link?url=HMqaMY4mXusH--4hMu1p6P_XUzEve9lZhFGtxScnbb8Z9HaLYJ981eWxAuZt2iAP ...
- AngularJS最理想开发工具WebStorm
http://blog.fens.me/angularjs-webstorm-ide/ Angularjs插件下载地址:http://plugins.jetbrains.com/plugin/6971 ...
- js架构设计模式——MVC,MVP 和 MVVM 的图示及简单明了的区别说明
MVC,MVP 和 MVVM 的图示 复杂的软件必须有清晰合理的架构,否则无法开发和维护. MVC(Model-View-Controller)是最常见的软件架构之一,业界有着广泛应用.它本身很容易理 ...
- adb server无法启动方法,结束占用端口的进程
adb server is out of date. killing...ADB server didn't ACK* failed to start daemon *error: unknown ...
- HTML5 新元素、HTML5 Canvas
HTML5 新元素 自1999年以后HTML 4.01 已经改变了很多,今天,在HTML 4.01中的几个已经被废弃,这些元素在HTML5中已经被删除或重新定义. 为了更好地处理今天的互联网应用,HT ...
- Bootstrap入门(十六)组件10:well和具有响应式特性的嵌入内容
Bootstrap入门(十六)组件10:well和具有响应式特性的嵌入内容 well组件可以为内容增添一种切入效果. 具有响应式特性的嵌入内容可以根据被嵌入内容的外部容器的宽度,自动创建一个固定的比例 ...
- 王爽汇编语言(第三版)环境搭建(附PDF及工具下载)
一.前言 最近在学习汇编语言,使用的是读者评价非常高的王爽老师写的<汇编语言>(第三版),为了适应现在各个版本的windows操作系统,所以采用VMWare虚拟机来搭建纯DOS环境. 二. ...