学习链接
异步迭代和 generator(Symbol.asyncIterator 和 for await ... of)
异步操作-async
异步的角度来看,async 算是 generator 的语法糖。
async 函数对 Generator 函数的改进,体现在以下四点。
(1)内置执行器
Generator 函数的执行需要调用
next方法,或者用co模块,也就是需要依靠执行器,才能真正执行,得到最后结果。async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。
(2)更好的语义
async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
(3)更广的适用性
co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象。而
async函数的await命令后面,可以是 Promise 对象和原始类型的值
(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)
(4)返回值是 Promise
async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。可以用then方法指定下一步的操作。进一步说,
async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。
语法

// 继发let foo = await getFoo();let bar = await getBar();// 执行完 getFoo() 才会执行 getBar()// 同时触发写法一let [foo, bar] = await Promise.all([getFoo(), getBar()]);// 同时触发写法二let fooPromise = getFoo();let barPromise = getBar();let foo = await fooPromise;let bar = await barPromise;
并发执行、继发执行
function dbFuc(db) { //这里不需要 asynclet docs = [{}, {}, {}];// 可能得到错误结果docs.forEach(async function (doc) {await db.post(doc);});}// 上面代码可能不会正常工作// 原因是这时三个db.post()操作将是并发执行,也就是同时执行,而不是继发执行// 改为 for 循环async function dbFuc(db) {let docs = [{}, {}, {}];for (let doc of docs) {await db.post(doc);}}// 用数组的 reduce() 方法async function dbFuc(db) {let docs = [{}, {}, {}];await docs.reduce(async (_, doc) => {await _;// 等待 上一次 值为 undefined 状态为 fulfilled 的对象await db.post(doc); // 异步操作// return undefined}, undefined);}
可保留运行堆栈
const a = () => {b().then(() => c());};
上面代码中,函数 a 内部运行了一个异步任务 b()。
当 b() 运行的时候,函数 a() 不会中断,而是继续执行。
等到 b() 运行结束,可能 a() 早就运行结束了,
b() 所在的上下文环境已经消失了。
如果 b() 或 c() 报错,错误堆栈将不包括 a()。
const a = async () => {await b();c();};
上面代码中,b() 运行的时候,a() 是暂停执行,上下文环境都保存着。一旦 b() 或 c() 报错,错误堆栈将包括 a()。
async 实现原理
将 Generator 函数和自动执行器包装在同一个函数中。
async function fn(args) {// ...}// 等同于function fn(args) {return spawn(function* () {// ...});}
function spawn(genF) {return new Promise((resolve, reject) => {const gen = genF();step(() => gen.next(undefined));function step(nextF) {let next;try {next = nextF();} catch (e) {return reject(e);}if (next.done) {return resolve(next.value);}Promise.resolve(next.value).then(v => { step(() => gen.next(v)); },e => { step(() => gen.throw(e)); });}});}
