[译] Async 函数,让promise更友爱!

原文链接

另,断断续续翻译了好几天,在宣布的时刻去搜刮了下有没人翻译了,由于这确实是篇好文章。还真有:文章链接,看了下,这篇翻译的专业些,人人能够去看看。

Async 函数是一个非常了不得的东西,它将会在Chrome 55中获得默许支撑。它许可你誊写基于promise的代码,但它看起来就跟同步的代码一样,而且不会壅塞主线程。所以,它让你的异步代码看起来并没有那末”智慧”却更具有可读性。

Async 函数的代码示例:

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

假如你在一个函数声明的的前面运用async关键字,那你就能够在这个函数内运用await。当你去await一个promise的时刻,这个函数将会以非壅塞的体式格局停息,直到promise处于settled状况。假如这个Promise返回的是胜利的状况,你将会获得返回值,假如返回的是失利的状况,那失利的信息将会被抛出。

提醒: 假如你对promises不熟习,请检察我们的promises指南

示例1: 打印相应信息

假定我们想要要求一个URL然后把相应信息打印出来,下面是运用promise的示例代码:

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.log('fetch failed', err);
  }
}

能够看到代码行数和上例一样,然则运用async函数的体式格局使得一切的回调函数都不见了!这让我们的代码非常轻易浏览,迥殊是那些对promise不是迥殊熟习的同砚。

提醒: 你await的任何值都是经由过程Promise.resolve()来通报的,所以你能够安全地运用非当地的promise.

Async 函数的返回值

不论你是不是在函数内部运用了await, Async 函数老是返回一个promise 。当 async函数显现滴返回恣意值时,返回的promise将会挪用resolve要领, 当async函数抛出非常错误时,返回的promise将会挪用reject要领,所以:

// wait ms milliseconds
function wait(ms) {
  return new Promise(r => setTimeout(r, ms));
}

async function hello() {
  await wait(500);
  return 'world';
}

当实行hello()时,返回一个胜利状况,而且通报的值为worldpromise.

async function foo() {
  await wait(500);
  throw Error('bar');
}

当实行foo()时,返回一个失利状况,而且通报的值为Error('bar')promise.

示例2: 相应流

在更复杂点的案例中, async函数更能表现其优越性。假定我们想要在纪录chunks数据时将其变成相应流, 并返回终究的信息长度。

提醒: “纪录chunks” 让我认为很别扭.

下面是运用promise的体式格局:

function getResponseSize(url) {
  return fetch(url).then(response => {
    const reader = response.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);
    })
  });
}

看清楚了,我是 promise “地下党” Jake Archibald。看到我是如何在它内部挪用 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;
    total += value.length;
    console.log('Received chunk', value);
    // get the next result
    result = await reader.read();
  }

  return total;
}

一切的”智慧”的代码都不见了。如今新的异步轮回运用了牢靠的,看起来一般的while轮回来替代,这使我认为非常的整齐。更多的是,在未来,我们将会运用async iterators,它将会运用for of轮回来替代while轮回,那这讲会变得越发整齐!

提醒: 我对streams比较有好感。假如你对streams不太熟习,能够看看我的指南

Async 函数的其他语法

我们已看过了async function() {} 的运用体式格局,然则async关键字还能够用于其他的函数语法中。

箭头函数
// map some URLs to json-promises
const jsonPromises = urls.map(async url => {
  const response = await fetch(url);
  return response.json();
});

提醒: array.map(func)不会在意你给的是不是是async函数,它只会把它当作一个返回值是promise的一般函数。所以,第二个回调的实行并不会守候第一个回调中的await处置惩罚完成。

对象要领
const storage = {
  async getAvatar(name) {
    const cache = await caches.open('avatars');
    return cache.match(`/avatars/${name}.jpg`);
  }
};

storage.getAvatar('jaffathecake').then(…);
类要领
class Storage {
  constructor() {
    this.cachePromise = caches.open('avatars');
  }

  async getAvatar(name) {
    const cache = await this.cachePromise;
    return cache.match(`/avatars/${name}.jpg`);
  }
}

const storage = new Storage();
storage.getAvatar('jaffathecake').then(…);

提醒: 类的 constructorsgetters/settings不能是 async 函数。

注重!请防止太甚强调递次

只管你正在写的代码看起来是同步的,但请确保你没有错失并行处置惩罚的时机。

async function series() {
  await wait(500);
  await wait(500);
  return "done!";
}

上面的代码须要 1000ms才完成,但是:

async function parallel() {
 const wait1 = wait(500);
 const wait2 = wait(500);
 await wait1;
 await wait2;
 return "done!";
}

上面的代码只须要500ms,由于两个wait在同一时间处置惩罚了。

示例3: 递次输出要求信息

假定我们想要猎取一系列的URL相应信息,并将它们尽量快的按准确的递次打印出来。
深呼吸….下面就是运用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());
}

Yeah, 这达到了目的。我正在用reduce来处置惩罚一串的promise,我太”智慧”了。这是一个云云”智慧”的代码,但我们最好不要如许做。

然则,当把上面的代码转换成运用 async函数来完成时,它看起来太有递次了,以至于会使我们很疑惑:

:-1: 不引荐 – 过于强调先后递次

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

看起来整齐多了,然则我的第二个要求只要在第一个要求被完整处置惩罚完成以后才会发出去,以此类推。这个比上面谁人promise的实例慢多了。幸亏这还有一个中立的计划:

:+1: 引荐 – 很好而且并行

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);
  }
}

在这个例子中,悉数的url一个接一个被要乞降处置惩罚,然则谁人’智慧的’的reduce被规范的,一般的和更具可读性的for loop 轮回庖代了。

浏览器兼容性和解决要领

在我写这篇文章时,Chrome 55已默许支撑async 函数。然则在一切主流浏览器中,它还在开辟中:

解决要领 1:Generators

一切的主流浏览器的最新版本都支撑generators,假如你正在运用它们,你能够稍稍polyfill一下 async函数.

Babel正能够为你做这些事变,这里有个经由过程Babel REPL写的示例 – 是不是是认为对转换后的代码很熟习。这个转换机制是 Babel’s es2017 preset的一部分。

提醒: Babel REPL是一个很风趣的东西,尝尝吧。

我发起你如今就如许做,由于当你的目的浏览器支撑了async函数时,你只须要将Babel从你的项目中去除即可。然则假如你真的不想运用转换东西,你能够运用Babel’s polyfill

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

当你运用了上面说的polyfill,你能够将上面的代码替换为:

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

注重到你经由过程给createAsyncFunction函数通报了一个generator (function*),然后运用yield 替代 await。除此之外它们的结果一样。

解决要领2: regenerator

假如你想要兼容旧的浏览器,Babel一样也能把generators给转换了,如许你就能够在IE8以上的浏览器中运用async函数,但你须要运用Babeles2017 presetthe es2015 preset

你会看到转换后的代码并不悦目,所以请警惕代码膨胀。

Async all the things!

一旦一切浏览器都支撑async函数了,请在一切返回值是promise的函数上运用async!由于它不仅能够使你的代码更tider, 而且它确保了async函数 老是返回一个 promise

回到 2014 年,我对async函数的涌现觉得非常冲动, 如今很愉快看到它们在浏览器中被支撑了。Whoop!

    原文作者:kraaas
    原文地址: https://segmentfault.com/a/1190000007368701
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞