评论

收藏

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

开发技术 开发技术 发布于:2021-12-27 13:40 | 阅读数:406 | 评论:0



一,前言
上一篇,主要介绍了 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[key](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;


关注下面的标签,发现更多相似文章