异步编程对于JavaScript是极为重要的。JavaScript只有一个线程,如果异步编程,得卡死,基本没法用。
ES6诞生之前,异步编程的方法大概有以下几种:
- 回调函数
- 事件监听
- 发布/订阅
- Promise对象
ES7中的async函数更是给出异步编程的终极解决方案
基本概念
异步
所谓“异步”,简单说就是一个任务分成两段执行,先执行第一段,然后转而执行其他任务,等做完准备工作再回过头执行第二段。
相应的,连续执行的就叫同步。
回调
所谓“回调”就是把任务的第二段单独写在一个函数中,等到重新执行该任务时直接调用这个函数。
读取文件进行处理是这样写的:
1 | fs.readFile('/etc/passwd', function(err, data) { |
为什么nodejs约定回调函数的第一个参数必须是错误对象err(如果没有错误,该参数就是null)?原因是执行拆分成两段,在这两段之间抛出的错误程序无法捕获,只能当做参数传入第二段
Promise
当回调函数过多,多重嵌套。代码不是纵向发展而是横向发展,很快代码就会乱成一团。Promise就是为了解决这个问题诞生的。
Promise不是新的语法功能,而是一种新的写法,允许将回调函数的横向加载改成纵向加载。
1 | var readFile = require('fs-readfile-promise') |
Promise最大的问题是代码冗余,原来的任务被Promise包装了一下,不管什么操作,一眼看去都是一堆then,原来的语义并不清楚。
Thunk函数
传名调用和传值调用
- 传值调用:在进入函数之前,就计算参数的值
1 | var x = 1 |
- 传名调用:传值调用比较简单,但是对参数求值时并未使用这个参数,有可能造成性能损失。而传名调用就是只在执行时求值
Thunk函数的含义
编译器“传名调用”实现往往是先将参数放到一个临时函数中,在将这个临时函数传入函数体。这个临时函数就叫Thunk
1 | function f(m) { |
async函数
async函数返回值是Promise,你可用then方法指定下一步操作。
async函数实现
async函数的实现就是将Generator函数和自动执行器包装在一个函数中1
2
3
4
5
6
7
8async function fn(args) {
//参数运算
}
//等同于
function fn(args) {
return spawn(function *)
}
asycn的用法
同Generator函数一样,async函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行时,一旦遇到await就会先返回,等到触发的异步操作完成,再接着执行函数体内后台的语句
1 | async function getStokPriceByName(name) { |
上面的代码是一个获取股票报价的函数,函数前面的async关键字表明该函数内部有异步操作。调用该函数时,会立即返回一个Promise对象。
下面这个例子指定了多少毫秒后输出一个值。
1 | function timeout((resolve) => { |
注意点
await命令后面的Promise对象,运行结果可能是Rejected,所以最好把await命令放在try…catch代码块中
1 | async function myFunction() { |