前言

2014年秋季写完了《深入理解javascript原型和闭包系列》,已经帮助过很多人走出了 js 原型、作用域、闭包的困惑,至今仍能经常受到好评的留言。

很早之前我就总结了JS三座大山这个概念(虽然没有到处宣扬),前两座(原型、作用域)已经基本讲明白,而第三座(异步)也应该做一个总结。

于是,2017年初春,我花费大约一周的业余时间来对 JS 异步做一个完整的总结,和各位同学共勉共进步!

原文地址:http://www.cnblogs.com/wangfupeng1988/p/6513070.html 未经同意禁止转载!

第一部分,什么是异步

提醒:如果你是初学 js 的同学,尚未有太多项目经验和基础知识,请就此打住,不要看这篇教程

我思考问题、写文章一般都不按讨论出牌,别人写过的东西我不会再照着抄一遍。因此,后面所有的内容,都是我看了许多资料之后,个人重新思考提炼总结出来的,这肯定不能算是初级教程。

如果你是已有 js 开发经验,并了解异步的基础知识,到这里来想深入了解一下Promise Generatorasync-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()需要传入两个参数进去,urlsuccess,其中url是请求的路由,success是一个函数。这个函数传递过去不会立即执行,而是等着请求成功之后才能执行。对于这种传递过去不执行,等出来结果之后再执行的函数,叫做callback,即回调函数

再看一段更加能说明回调函数的 nodejs 代码。和上面代码基本一样,唯一区别就是:上面代码时网络请求,而下面代码时 IO 操作。

var fs = require('fs')
fs.readFile('data1.json', (err, data) => {
console.log(data.toString())
})

从上面两个 demo 看来,实现异步的最核心原理,就是将callback作为参数传递给异步执行函数,当有结果返回之后再触发 callback执行,就是如此简单!

常用的异步操作

开发中比较常用的异步操作有:

  • 网络请求,如ajax http.get
  • IO 操作,如readFile readdir
  • 定时函数,如setTimeout setInterval

最后,请思考,事件绑定是不是也是异步操作?例如$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)—— 什么是异步的更多相关文章

  1. 深入理解javascript函数系列第一篇——函数概述

    × 目录 [1]定义 [2]返回值 [3]调用 前面的话 函数对任何一门语言来说都是一个核心的概念.通过函数可以封装任意多条语句,而且可以在任何地方.任何时候调用执行.在javascript里,函数即 ...

  2. 深入理解javascript函数系列第二篇——函数参数

    × 目录 [1]arguments [2]内部属性 [3]函数重载[4]参数传递 前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传 ...

  3. 深入理解javascript作用域系列第一篇——内部原理

    × 目录 [1]编译 [2]执行 [3]查询[4]嵌套[5]异常[6]原理 前面的话 javascript拥有一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域.作用域 ...

  4. 深入理解javascript作用域系列第二篇——词法作用域和动态作用域

    × 目录 [1]词法 [2]动态 前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极 ...

  5. 深入理解javascript函数系列第三篇——属性和方法

    × 目录 [1]属性 [2]方法 前面的话 函数是javascript中的特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本 ...

  6. 深入理解javascript作用域系列第四篇——块作用域

    × 目录 [1]let [2]const [3]try 前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用 ...

  7. 深入理解javascript作用域系列第三篇——声明提升(hoisting)

    × 目录 [1]变量 [2]函数 [3]优先 前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javasc ...

  8. 深入理解javascript作用域系列第三篇

    前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javascript作用域系列第三篇——声明提升(hois ...

  9. 深入理解javascript作用域系列第二篇

    前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极易出错.这实际上是由两种作用域工作 ...

  10. 深入理解javascript作用域系列第一篇

    前面的话 javascript拥有一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域.作用域貌似简单,实则复杂,由于作用域与this机制非常容易混淆,使得理解作用域的原 ...

随机推荐

  1. WebStorm界面出现中文乱码(出现口口口)

    不少刚刚使用WebStorm软件的童鞋,发现在新建一个项目时,如果输入中文,会显示成口口口.这个问题要怎么解决呢... 点一下界面上那个扳手图标(settings),快捷键Ctrl+Alt+S. 2 ...

  2. 2.11. 创建托管对象(Core Data 应用程序实践指南)

    到现在为止,还没有创建过托管对象,回顾了一下,只是创建了托管对象模型,持久化存储区,持久化存储协调器,托管对象上下文. 那么,现在就来创建托管对象吧. 使用NSEntityDescription 的 ...

  3. 如何在windows下载和安装Apache

    进入apache服务器官网http://httpd.apache.org/,这里我们以下载稳定版的httpd 2.4.25为例,点击"Files for Microsoft Windows& ...

  4. JS 工具 构建工具

    1.gruntjs http://www.gruntjs.net/ 2.bootstrap http://www.bootcss.com/ 3.

  5. TIMESTAMP和DATETIME哪个好

    日期范围 TIMESTAMP 支持从'1970-01-01 00:00:01′ 到 '2038-01-19 03:14:07′ UTC. 这个时间可能对目前正在工作的人来说没什么问题,可以坚持到我们退 ...

  6. iOS多线程NSThread和GCD

    在iOS中啊  其实有多种方法实现多线程 这里只记录两个比较常用的  或者说我比较常用的 一个就是BSThread 另一个就是一听名字就比较霸气的妇孺皆知的GCD 先说一下NSThread吧 这个方式 ...

  7. 使用Flex构建Web和移动参考应用程序

    范例文件 Shopping Cart Sales Dashboard Expense Tracker 需要的其他产品 Android 2.2及更高版本或Android 3.0及更高版本的设备 仅仅在F ...

  8. C++实现具有基本功能的智能指针

    C++中的智能指针实际上是代理模式与RAII的结合. 自定义unique_ptr,主要是release()和reset().代码如下. #include <iostream> using ...

  9. jqGrid Demos

    http://www.trirand.com/blog/jqgrid/jqgrid.html http://www.cnblogs.com/huozhicheng/archive/2012/11/11 ...

  10. B+树的插入、删除(附源代码)

    B+ Tree Index B+树的插入 B+树的删除 完整测试代码 Basic B+树和B树类似(有关B树:http://www.cnblogs.com/YuNanlong/p/6354029.ht ...