异步操作和Async函数

2020-08-10  本文已影响0人  zhulichao

异步操作和Async函数

异步操作

异步编程的四种方式:回调函数、事件监听、发布/订阅、Promise。

为什么Node约定,回调函数的第一个参数,必须是错误对象err(如果没有错误,该参数就是null)?原因是执行分成两段,第一段执行完以后,任务所在的上下文环境就已经结束了,在这以后抛出的错误,原来的上下文环境已经无法捕捉,只能当作参数,传入第二段。

回调函数在多个回调函数嵌套时,代码不是纵向发展,而是横向发展,很快就会乱成一团,无法管理,称为"回调函数地狱"。Promise对象就是为了解决这个问题而提出的,将回调函数的嵌套,改成链式调用。

Promise的写法只是回调函数的改进,使用then方法以后,异步任务的两段执行看得更清楚了,除此以外,并无新意。Promise的最大问题是代码冗余,不管什么操作,一眼看去都是一堆then,原来的语义变得很不清楚。

Generator函数可以暂停执行和恢复执行,这是它能封装异步任务的根本原因。除此之外,它还有两个特性,使它可以作为异步编程的完整解决方案:函数体内外的数据交换和错误处理机制。

使用Generator函数,执行一个异步任务。

var fetch = require('node-fetch');

function* gen(){
  var url = 'https://api.github.com/users/github';
  var result = yield fetch(url);
  console.log(result.bio);
}
/*
 * 执行Generator函数,获取遍历器对象,然后用next方法执行异步任务的第一阶段。
 * 由于Fetch模块返回的是一个Promise对象,因此要用then方法调用下一个next方法。
 */
var g = gen();
var result = g.next();
result.value.then(function(data){
  return data.json();
}).then(function(data){
  g.next(data);
});

Thunk函数现在可以用于Generator函数的自动流程管理。下面就是一个基于Thunk函数的Generator执行器。

function run(fn) {
  var gen = fn();
  // next函数是Thunk的回调函数
  function next(err, data) {
    var result = gen.next(data);
    if (result.done) return;
    result.value(next);
  }
  next();
}

function* g() {
  // ...
}
run(g);

Async函数

从语法上看,async函数就是将Generator函数的星号(*)替换成async,将yield替换成await。async函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。

async函数对 Generator 函数的改进,体现在以下四点:

async函数的语法规则总体上比较简单,难点是错误处理机制:

async函数的实现

async function fn(args){
  // ...
}

// 等同于
function fn(args){
  return spawn(function*() {
    // ...
  });
}
function spawn(genF) {
  return new Promise(function(resolve, reject) {
    var gen = genF();
    function step(nextF) {
      try {
        var next = nextF();
      } catch(e) {
        return reject(e);
      }
      if(next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}

async 函数的用法

async function getStockPriceByName(name) {
  const symbol = await getStockSymbol(name);
  const stockPrice = await getStockPrice(symbol);
  return stockPrice;
}

getStockPriceByName('goog').then(function (result) {
  console.log(result);
});
上一篇下一篇

猜你喜欢

热点阅读