一、什么是异步
1. JS为何会有异步?
JS是单线程语言。例如如下示例,单线程就是程序一行行执行,如果上面一行没执行完,那么就一直等执行完成后才去继续执行下一行。
1 | var i, t = Date.now() |
上述情况勉强还可接受,但是对于请求网络资源,接口未返回数据时就一直傻傻等着那肯定不行。因此对于这种场景,JS就设计了异步。
2. 异步的实现原理?
1 | var ajax = $.ajax({ |
1 | var fs = require('fs') |
从上面两个 demo 看来,实现异步的最核心原理,就是将callback作为参数传递给异步执行函数,当有结果返回之后再触发 callback执行,就是如此简单!
3. 常用的异步操作?
(1)网络请求
(2)I/O操作
(3)定时函数:setTimeout、setInterval
二、异步和EventLoop
三、JS异步操作
1. 传统的$.ajax:
1 | // 1. 使用 success() 和 error() 回调函数 |
2. 1.5版本后的$.ajax:
1 | // 1. 可以链式的执行 done() 或 fail() |
(1)改进之后的好处:
- 1.5版本前的ajax和1.5之后的ajax的主要区别是$.ajax()返回值不同,1.5前是返回一个xhr,1.5之后是返回deferred对象,而这个deferred对象带有done() 和 fail()方法,而这两个方法封装了回调,都是在请求结果后才去自动调用的。这种方式为之后Promise标准的制定提供了很大的参考意义,deferred可以认为是Promise的前身。
- 虽然 JS 是异步执行的语言,但是人的思维是同步的。因此,开发者总是在寻求如何使用逻辑上看似同步的代码来完成 JS 的异步请求。而 jquery 的这一次更新,让开发者在一定程度上得到了这样的好处。
- 之前无论是什么操作,我都需要一股脑写到callback中,现在不用了。现在成功了就写到done中,失败了就写到fail中,如果成功了有多个步骤的操作,那我就写很多个done,然后链式连接起来就 OK 了。
(2)jquery实现异步的原理:
jquery 通过deferred对象解决了异步中callback函数的问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14function waitHandle() {
var dtd = $.Deferred() // $.Deferred() 会返回一个 deferred 对象。deferred对象有done、fail、then方法,还有resolve、reject方法,前三个方法是状态变化之后才会触发的监听函数,后两个方法是主动触发用来改变状态。
var wait = function (dtd) { // 要求传入一个 deferred 对象
var task = function () {
console.log('执行完成')
dtd.resolve() // 表示异步任务已经完成
}
setTimeout(task, 2000)
return dtd // 要求返回 deferred 对象,再次返回deferred对象的目的是可以链式调用
}
// 注意,这里一定要有返回值
return wait(dtd)
}由于deferred有五个方法,但其中的resolve和reject只能是主动触发来修改状态。但是上述程序最终返回的结果是deferred对象,这样外部就可以调用resolve和reject,而产生意想不到的后果。所以我们优化一下:
这边利用deferred对象的promise对象没有resolve和reject方法,而只有done、fail、then。
1 | function waitHandle() { |
3. Promise 实现异步
promise的出现离不开jquery中ajax的实现思想,所以上述了解ajax1.5前后的版本,及实现原理都是为了更好的理解Promise。
(详情请看下节:深入理解JavaScript异步(二、Promise))
4. ES6中的Generator
(详情请看下节:深入理解JavaScript异步(三、generator))
5. ES7中的async-await:Generator的语法糖
(详情请看下节:深入理解JavaScript异步(三、Async/Await))
参考: