深入理解 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
执行,就是如此简单!
常用的异步操作
开发中比较常用的异步操作有:
- 网络请求,如
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源码解读》