飞奔的炮台 发表于 2021-12-27 13:40:20

#yyds干货盘点#【Promise 源码学习】第十七篇 - async/await 简介



一,前言
上一篇,主要介绍了 co 库的使用和实现原理,主要涉及以下几个点:

[*]co 库的简介:特性、用法、功能分析;
[*]co 库的实现和原理分析;
本篇,继续介绍 async/await:

[*]async/await 是基于 generator 的语法糖
[*]async/await 是 generator + co 的组合;
[*]async/await 是“回调地狱”问题的终极解决方案;

二,async/await 的使用

[*]测试场景:读取 a.txt得到结果 b.txt;读取 b.txt得到结果 c;

const util = require('util');
const fs = require('fs');
let readFile = util.promisify(fs.readFile)

// generator 生成器函数
function * read() {
let data = yield readFile('./a.txt','utf8');
data = yield readFile(data,'utf8');
return data;
}

// 方法一:Generator 生成器
let it = read(); // 生成器函数返回一个 Iterator 迭代器对象
let {value:v1, done:d1} = it.next(); // 注意:第一个next传参是无效的
v1.then(data=>{
console.log(data) // b.txt
let {value:v2, done:d2} = it.next(data); // 将第一次的结果作为第二次的入参
return v2
}).then(data=>{
console.log(data) // 执行结果:c
})

// 方法二:使用 co 库
const co = require('co')
// co 方法:包装 generator 生成器函数,内部自动执行完成,返回一个 promise
co(read()).then(data=>{
console.log(data);// 执行结果:c
})

// 方法三:使用 async/await
async function read() {
let data = await readFile('./a.txt','utf8');
data = await readFile(data,'utf8');
return data;
}

// async 函数执行完成后,返回一个 promise
read().then(data=>{
console.log(data)
})之前,使用 generator + co 组合,已经让代码看上去很像是同步了,但仍需要 co 库的支持;使用 async/await:在外层 function 前添加 async;await 右侧的方法返回 promise;无需 co 库支持即可实现;这样,代码看上去与“同步编码”无任何明显差异,有效避免了嵌套回调问题;

三,async/await 的实现
还是将 async/await 代码放入 babel.io,查看编译后的代码:
"use strict";

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
    var info = gen(arg);
    var value = info.value;
} catch (error) {
    reject(error);
    return;
}
if (info.done) {
    resolve(value);
} else {
    Promise.resolve(value).then(_next, _throw);
}
}

function _asyncToGenerator(fn) {
return function () {
    var self = this, args = arguments;
    return new Promise(function (resolve, reject) {
      var gen = fn.apply(self, args);
      function _next(value) {
      asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
      } function _throw(err) {
      asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
      } _next(undefined);
    });
};
}

function read() {
return _read.apply(this, arguments);
}

function _read() {
_read = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
    var data;
    return regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
      switch (_context.prev = _context.next) {
          case 0:
            _context.next = 2;
            return readFile('./a.txt', 'utf8');

          case 2:
            data = _context.sent;
            _context.next = 5;
            return readFile(data, 'utf8');

          case 5:
            data = _context.sent;
            return _context.abrupt("return", data);

          case 7:
          case "end":
            return _context.stop();
      }
      }
    }, _callee);
}));
return _read.apply(this, arguments);
}以上代码可以看出,_read函数就是之前实现的 generator 函数,这就说明 async 最终会被转换成为 generator 生成器;执行过程分析:

[*]第一次调用异步迭代方法,传入 undefined:_next(undefined);,进入asyncGeneratorStep方法:gen 是 it 迭代器,key 是 next 方法;
[*]如果没完成,继续调用_next(递归),继续取出下一个执行异步迭代;
所以,async/await 其实也就是 generator + co 的语法糖;编码中,所有异步回调代码可以全部采用 async/await 写法替换,可以有效解决回调嵌套问题;

四,结尾
本篇,主要介绍了 async/await 的使用和实现原理,主要涉及以下几个点:

[*]async/await 的使用和功能分析;
[*]async/await 的实现和原理分析;
下一篇,继续介绍浏览器的事件环 EventLoop;


https://blog.51cto.com/u_7466756/4847010
页: [1]
查看完整版本: #yyds干货盘点#【Promise 源码学习】第十七篇 - async/await 简介