深入理解 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源码解读》
深入理解 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机制非常容易混淆,使得理解作用域的原 ...
随机推荐
- 前言(Core Data 应用开发实践指南)
Core Data 并不是数据库,它其实是一个拥有多种功能的框架.其中,有个功能是把程序与数据库之间的交互过程自动化,不用再编写SQL代码,改用Objective-C对象来实现. Core Data ...
- ios自定义UIButton内部空间Rect
- 我用Cocos2d-x模拟《Love Live!学院偶像祭》的Live场景(四)
[前言和思路整理] 千呼万唤Shǐ出来!最近莫名被基友忽悠着进舰坑了,加上要肝LL活动,又碰上公司项目紧张经常加班,这一章发得比以往时候来得更晚一些,抱歉啊. 上一章我们实现了BeatObjectMa ...
- Mac 电脑系统的重装
首先来说一下我为什么会想到重装Mac的系统呢??? 其实呢 很简单的一个理由,在我写上一个项目的时候,在功能code编写完成后,在模拟器上运行是完全没有问题的,但是在真机上就不行,大家也都知道,在X ...
- storm遇到问题汇总
http://www.reader8.cn/jiaocheng/20131023/2139887.html 错误1:在windows下运行ExclamationTopology或者WordCountT ...
- Java泛型中的通配符的使用
package com.srie.testjava; import java.util.ArrayList; import java.util.List; public class TestClass ...
- 使SSH不用输入密码
1. 自动ssh/scp方法== A为本地主机(即用于控制其他主机的机器) ;B为远程主机(即被控制的机器Server), 假如ip为192.168.60.110;A和B的系统都是Linux 在A上运 ...
- .net 开发经理的月薪
因为各人的成长不一样,有人工作了5年,技术也只能当个高级程序员,有人工作了两年,就能带领一个团队,有人在初中时期就写了一个很牛X 的框架,而我工作也快5年,也努力奋斗了5年,我觉得自己有能力做开发经理 ...
- json-lib之复杂数据类型的转换
在json字符串转java bean时,一般的对象,可以直接转,如:一个学生类,属性有姓名.年龄等 public class Student{ private String sname; privat ...
- Java之英格玛简单实现以及加密验证码的应用
最近看了一部电影<模仿游戏>,<模仿游戏>中艾伦·图灵破译英格玛让我对英格玛产生了好奇,于是就开始翻阅资料对其进行研究,但是毕竟智慧有限,所以我这里用Java实现一个简单的英格 ...