JavaScript异步编程

同步:按顺序执行代码,当前代码执行完毕才会执行后续。

异步:执行到异步代码时,把异步代码打包成任务放入任务队列后,就继续执行后续代码,不会等待异步代码执行完毕。

实现异步的四种方法:

  • 回调函数

  • Promise:ES6

  • Generator:ES6

  • async/await:ES7,终极方案

(1)回调函数

(2)Promise

Promise 是 ES6 加入的。

1
2
3
4
5
6
7
8
9
10
11
// 1. 创建Promise对象
let promise = new Promise((resolve) => {
let data = doSomething();
// 传回数据
resolve(data);
});

// 2. 通过promise.then()获取数据
promise.then((data)=>{

})

(3)Generator

Generator 用于更方便的创建 iterator(迭代器)。

Generator 相当于 ES6 的协程。

协程:可暂停执行过程的函数。

任何数据结构只要部署了 Iterator 接口,就可以完成遍历操作。

简单使用

1
2
3
4
5
6
7
8
9
// 定义 Generator 函数
//
function* buildIterator(){
yield 1;
yield 2;
}
let iterator = buildIterator();
iterator.next();
iterator.next();

迭代器内部的 yield 语句给迭代器返回值,并暂停代码执行。

调用迭代器的 next() 方法,迭代器内部代码继续执行,直到 yield 语句再次暂停。

Generator 函数本身跟异步是没有关系的,但使用它可以实现异步编程。

示例,使用 Generator 实现 async/await:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 执行async函数
function runCoroutine(generator) {
const iterator = generator();
// 内部函数,执行下一步,value是上一次等待的结果
function step(value) {
const res = iterator.next(value);
console.log("yield结果", res);
if (res.done) {
return Promise.resolve(value);
}
if (res.value instanceof Promise) {
return res.value.then(step);
} else {
return step(res.value);
}
}
return step();
}

(4)async/await

async/await 是 JS 异步编程的终极方案。

本质上它们是 Generator 的语法糖。await 相当于 yield Promise,等 Promise 执行结束后调用 next(),继续执行后续代码。

async/await 一般是与 Promise 配合使用的。

async 函数

async 函数的返回结果是一个 Promise 对象。

1
2
3
async function test(){

}

async 函数的执行与返回结果的关系:

  • 非 Promise 类型数据:Promise 成功
  • Promise 对象:由 Promise 对象的结果决定
  • 抛出异常:Promise 失败

await 表达式

await 右侧的表达式一般为 Promise 对象,此时 await 返回的是 promise 成功的值,如果 promise 失败则会抛出异常。

如果 await 右侧是一个非 Promise 值,则直接返回该值。

await 主要是为了:获取 Promise 的成功值。

await 必须写在 async 函数中,但 async 函数中可以没有 await 表达式。

辅助函数

Node.js 的核心模块 util 中提供了一个方法util.promisify(),可以将基于回调的函数转换为基于 Promise 的函数。

1
2
3
4
const fs = require('fs');
const util = require('util');
// 将fs模块中的readFile函数Promise化
const readFilePromise = util.promisify(fs.readFile);

实践:合并多个文件

读取文件(回调方式)

1
2
3
4
5
6
7
8
9
function readFile(){
fs.readFile('./res/file1.txt', (err1, data1) => {
fs.readFile('./res/file2.txt', (err2, data2) => {
fs.readFile('./res/file3.txt', (err3, data3) => {
console.log(data1 + '\n' + data2 + '\n' + data3);
});
})
});
}

读取文件(async与await)

1
2
3
4
5
6
async function readFile(){
let data1 = await readFilePromise('./res/file1.txt');
let data2 = await readFilePromise('./res/file2.txt');
let data3 = await readFilePromise('./res/file3.txt');
console.log(data1 + '\n' + data2 + '\n' + data3);
}

简洁,没有回调函数嵌套。