简单来说,异步编程就是在执行一个指令之后不是马上得到结果,而是继续执行后面的指令,等到特定的事件触发后,才得到结果。用 JavaScript 构建一个应用的时候经常会遇到异步编程,不管是 Node 服务端还是 Web 前端。
那如何去进行异步编程呢?就目前的标准以及草案来看,主要有下面的几种方式:
- 回调
- promise
- Generator
- await/async
一 、回调
回调函数是一段可执行的代码段,它作为一个参数传递给其他的代码,其作用是在需要的时候方便调用这段(回调函数)代码。
在JavaScript中函数也是对象的一种,同样对象可以作为参数传递给函数,因此函数也可以作为参数传递给另外一个函数,这个作为参数的函数就是回调函数。
我们在调用他的时候,不会马上得到结果,而是会继续执行后面的代码。这样,如果我们需要在查到结果之后才做某些事情的话,就需要把相关的代码写在回调里面,如果涉及到多个这样的异步操作,就势必会陷入到回调地狱中去。
回调什么时候执行
回调函数,一般在同步情境下是最后执行的,而在异步情境下有可能不执行,因为事件没有被触发或者条件不满足。
回调函数的使用场合
-
资源加载:动态加载 js 文件后执行回调,加载 iframe 后执行回调,ajax 操作回调,图片加载完成执行回调,AJAX 等等。
-
DOM 事件及 Node.js 事件基于回调机制(Node.js 回调可能会出现多层回调嵌套的问题)。
-
setTimeout 的延迟时间为 0,这个 hack 经常被用到,settimeout 调用的函数其实就是一个 callback 的体现
-
链式调用:链式调用的时候,在赋值器(setter) 法中(或者本身没有返回值的方法中)很容易实现链式调用,而取值器(getter)相对来说不好实现链式调用,因为你需要取值器返回你需要的数据而不是 this 指针,如果要实现链式方法,可以用回调函数来实现。
-
setTimeout、setInterval 的函数调用得到其返回值。由于两个函数都是异步的,即:他们的调用时序和程序的主流程是相对独立的,所以没有办法在主体里面等待它们的返回值,它们被打开的时候程序也不会停下来等待,否则也就失去了 setTimeout 及 setInterval 的意义了,所以用 return 已经没有意义,只能使用 callback。callback 的意义在于将 timer 执行的结果通知给代理函数进行及时处理。
例如:
var friends = ["Mike", "Stacy", "Andy", "Rick"];
friends.forEach(function (eachName, index){
console.log(index + 1 + ". " + eachName); // 1. Mike, 2. Stacy, 3. Andy, 4. Rick
});
二、Promise
Promise 是 ES 2015 原生支持的,他把原来嵌套的回调改为了级联的方式。
一般着,我们对一个 Promise 可以这样写:
var a = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('1')
}, 2000)
})
a.then(function(val) {
console.log(val)
})
如果要涉及到多个异步操作的顺序执行问题,我们可以这样写:
var a = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('1')
}, 2000)
})
a
.then(function(val){
console.log(val)
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('2')
}, 2000)
})
})
.then(function(val) {
console.log(val)
})
我们只需要 return 一个 Promise 即可实现这种多个异步操作的顺序执行。
三、Generator
在 ES 2015 中,出现了 Generator 的语法。
简单来说,Generator 可以理解为一个可以遍历的状态机,调用 next 就可以切换到下一个状态。
在 JavaScript 中,Generator 的 function 与 函数名之间有一个 *, 函数内部使用 yield 关键词,定义不同的状态。
四、await/async
这是在 ES 2016 中引入的新关键词,这将在语言层面彻底解决 JavaScript 的异步回调问题,目前可以借助 babel 在生产环境中使用。使用 await/async 可以让异步的操作以同步的方式来写。
相比较来说,await/async 解决了完全使用 Promise 的一个极大痛点——不同Promise之间共享数据问题:
Promise 需要设定外层数据开始共享,这样就需要在每个then里面进行赋值,而 await/async 就不存在这样的问题,只需要以同步的方式去写就可以了。
最后借用阮一峰老师微博上的图片来比较各异步的写法:



网友评论