高阶Promise--async

利用异步函数async,可以像写同步代码那样写基于Promise的代码,还可以让异步代码主线程.

await某个Promise时,函数暂停执行,直到该Promise产生结果,并且暂停并不会重启主线程.

async的工作方式:

async function myFirstAsyncFunction(){
  try {
    const fulfilledValue = await promise;
  }
  catch (rejectedValue) {
    // ...
  }
}

示例: 通过fetch获取网络日志

之前:

function logFetch(url) {
  return fetch(url)
    .then(response => response.text())
    .then(text => {
      console.log(text);
    }).catch(err => {
      console.error('fetch failed', err)
    });
}

使用async之后:

async function logFetch(url){
  try {
    const response = await fetch(url);
    console.log(await response.text());
  }
  catch (err) {
    console.error('fetch failed', err)
  }
}

使用 async 相较 promise,去掉了所有回调.

示例: 流式传输响应

在流式传输响应的同时,记录数据块日志,并返回数据块最终大小.

之前:

function getResponseSize(url){
  return fetch(url).then(response => {
    const reader = reponse.body.getReader();
    let total = 0;

    return reader.read().then(function processResult(result) {
        if (result.done) return total;
        
        const value = result.value;
        total += value.length;
        console.log('Received chunk', value);
    
        return reader.read().then(processResult);
    })
  });
}

这是一段精巧的代码,在 processResult 内部调用其自身来建立异步循环,看起来很只能,但是得盯着看上一会儿才能明白其作用.

使用async之后:

async function getResponseSize(url) {
  const response = await fetch(url);
  const reader = response.body.getReader();
  let result = await reader.read();
  let total = 0;

  while (!result.done) {
    const value = result.value;
    tatol += value.length;
    console.log('Received chunk', value);
    // get the next result
    result = await reader.read();
  }
  
  return total;
}

让人大有飘飘然的异步循环被替换成可靠却单调乏味的while循环,但简明性大幅提升.

未来,将使用异步迭代器来将 while 循环替换成 for-of 循环,从而进一步提高代码简明性.

其它使用async的场景,在剪头函数中,对象方法中,类方法中.类构造函数以及getter/setter方法中不能是异步的.

弊端写法,过于循序.示例: 获取一系列网址并尽快按照正确顺序将其记录到日志中

[不推荐]promise写法

function logInOrder(urls) {
  // fetch all the URLs
  const textPromises = urls.map(url => {
    return fetch(url).then(response => response.text());
  });
  // log them in order
  textPromises.reduce((chain, textPromise) => {
    return chain.then(() => textPromise)
      .then(text => console.log(text));
  }, Promise.resolve());
}

上面还使用了智能的reduce来对Promise序列进行链接,但这种编码过于"智能"还是不要为好.

使用async简单改写以上代码会让代码变得过于循序.

[不推荐]async的过于循序写法

async function logInOrder(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    console.log(await response.text());
  }
}

代码简洁不少,但我们想要的第二次获取必须等到第一次获取完毕之后才能开始,执行效率比并行执行获取的Promise低得多.

进一步使用async改写,使其并行效率高:

async function logInOrder(urls) {
  // fetch all the URLs in parallel
  const textPromises = urls.map(async url => {
    const response = await fetch(url);
    return response.text();
  });

  // log them in sequence
  for (const textPromise of textPromises) {
    console.log(await textPromise);
  }
}

以并行方式获取和读取网址,但将"智能的"reduce替换成了标准的单调的乏味的但可读性强的for循环.

使用generator生成器进行polyfill

原先异步代码:

async function slowEcho(val) {
  await wait(1000);
  return val;
}

进行polyfill:

const slowEcho = createAsyncFunction(function*(val){
  yield wait(1000);
  return val;
})

将生成器(function*)传给createAsyncFuntion,并使用yield来代替await.

babel里polyfill await的工作方式

简单async代码:

async function logFetch(url) {
  try {
    const response = await fetch(url);
    console.log(await response.text());
  }
  catch (err) {
    console.log('fetch failed', err);
  }
}

转换后:

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 logFetch(_x) {
  return _logFetch.apply(this, arguments);
}

function _logFetch() {
  _logFetch = _asyncToGenerator(function* (url) {
    try {
      const response = yield fetch(url);
      console.log(yield response.text());
    } catch (err) {
      console.log('fetch failed', err);
    }
  });
  return _logFetch.apply(this, arguments);
}
上一篇:springboot:嵌套使用异步注解@Async还会异步执行吗


下一篇:js中解决异步编程的方案