资讯专栏INFORMATION COLUMN

Promises与 async/await

beita / 2882人阅读

摘要:是一种与协作的特殊的语法。换句话说,仅可以运行在中。所以将会进行等待,而之后抛出一个错误。同时这也将更为便利。允许在函数内部使用。关键词确保运行时将会等待处理完毕,并且如果触发了一个运行错误,运行中断,并在改处类似触发。

“async/await”是一种与“promise”协作的特殊的语法。它使得异步工作更加容易理解和使用。

Async函数

我们从async关键词开始,它可以被放置在任何函数的开头位置,比如:

async function f() {
  return 1;
}

这里的async表示:该函数将始终返回一个promise。即使您的代码没有显式返回一个promise,在JavaScript运行时也会自动包装一个promise,用于返回指定的值。

在这个例子中,这段代码将会返回一个result为1的promise

async function f() {
  return 1;
}

f().then(alert); // 1

当然,我们也可以显式的返回一个promise:

async function f() {
  return Promise.resolve(1);
}

f().then(alert); // 1

async确保了函数会返回一个promise。挺简单的对吧?接下来,是另一个关键词await,仅仅能在async标记的函数中生效。

Await

语法说明:

//该段代码仅仅能在 async 标记的函数中生效
let value = await promise;

关键词await确保JavaScript运行时将会等待promise执行完毕并返回结果。
下面是一段使用promise并在一秒后返回结果的例子:

async function f() {

  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("done!"), 1000)
  });

  let result = await promise; // 等待至promise获得结果 (*)

  alert(result); // "done!"
}

f();

该函数在运行至await时,执行了“pauses”操作,直至promise执行完毕后重新执行接下来的代码。所以该段代码将在一秒后显示“done!”。
让我们强调一遍:await将顺序使得JavaScript的运行时等待promise执行完毕,而后继续运行余下代码。等待时的操作并不会消耗任何CPU资源,因为此时的运行时可以同时执行其他操作:执行其他的代码,处理事件逻辑等。
这仅仅是一项相对promise.than更为优雅的语法来获取promise的运行结果,更容易阅读和编写代码而已。

不能将await用于任何标准函数

如果您尝试将await运行在任何未标记为async的函数中,都会产生一个语法错误

function f() {
  let promise = Promise.resolve(1);
  let result = await promise; // Syntax error
}

我们如果没有使用async标记函数,那么我们就会得到这个语法错误。换句话说,await仅可以运行在async function中。

让我们修改 Promises chaining 中的例子,使用async/await来重写这个例子。

我们需要将.then替换为await

我们需要将函数修改为async function

async function showAvatar() {

  // read our JSON
  let response = await fetch("/article/promise-chaining/user.json");
  let user = await response.json();

  // read github user
  let githubResponse = await fetch(`https://api.github.com/users/${user.name}`);
  let githubUser = await githubResponse.json();

  // show the avatar
  let img = document.createElement("img");
  img.src = githubUser.avatar_url;
  img.className = "promise-avatar-example";
  document.body.append(img);

  // wait 3 seconds
  await new Promise((resolve, reject) => setTimeout(resolve, 3000));

  img.remove();

  return githubUser;
}

showAvatar();

相当容易阅读和理解对吧。

await 并不能在顶层环境中生效

人们在开始使用await时,总是容易忘记必须在async function内部使用。比如以下代码将会报错:

// syntax error in top-level code
let response = await fetch("/article/promise-chaining/user.json");
let user = await response.json();

所以我们需要声明一个async function来包裹该段代码。

await 可以接受 thenables

类似promise.thenawait准许使用then方法。需要申明的是,这里指的是一个非promise对象,但它持有.then方法,那么就可以配合await使用。
比如以下例子,await接受new Thenable(1):

class Thenable {
  constructor(num) {
    this.num = num;
  }
  then(resolve, reject) {
    alert(resolve); // function() { native code }
    // resolve with this.num*2 after 1000ms
    setTimeout(() => resolve(this.num * 2), 1000); // (*)
  }
};

async function f() {
  // waits for 1 second, then result becomes 2
  let result = await new Thenable(1);
  alert(result);
}

f();

如果await与一个含有.then方法的非promise对象组合,同时其支持resolvereject两个方法作为参数。那么await将会等待其中之一的函数被调用(注释(*)所在行)并在之后继续运行剩余代码。

Async方法

类函数亦可以被定义为异步函数,仅需将async置于函数声明前。
类似于:

class Waiter {
  async wait() {
    return await Promise.resolve(1);
  }
}

new Waiter()
  .wait()
  .then(alert); // 1

这和之前的其他代码端是一样的作用:应用await返回一个promise对象。

错误处理

如果promise顺利完成,那么await promise将返回一个值。但假如触发了错误,那么将在此行代码中throw一个错误。

以下代码:

async function f() {
  await Promise.reject(new Error("Whoops!"));
}

等同于:

async function f() {
  throw new Error("Whoops!");
}

在真实的运行环境中,可能需要消耗一些时间来触发错误。所以await将会进行等待,而之后抛出一个错误。我们可以通过try…catch来捕获错误,同样的方法也适用于throw:

async function f() {

  try {
    let response = await fetch("http://no-such-url");
  } catch(err) {
    alert(err); // TypeError: failed to fetch
  }
}

f();

触发了错误之后,运行代码将跳转至catch代码块。我们可以使用如下代码:

async function f() {

  try {
    let response = await fetch("/no-user-here");
    let user = await response.json();
  } catch(err) {
    // catches errors both in fetch and response.json
    alert(err);
  }
}

f();

但如果我们没有使用try…catch,那么将会在异步调用f()时触发rejected。我们也可以添加.catch来处理错误:

async function f() {
  let response = await fetch("http://no-such-url");
}

// f() becomes a rejected promise
f().catch(alert); // TypeError: failed to fetch // (*)

如果我们忘记添加.catch,我们将获得一个未被捕获的错误。我们也可以使用一个全局事件捕获方法来处理,参见Promise chaining。

async/await 和 promise.then/catch

当我们使用async/await时,我们仅仅需要.then,因为await会接受正确结果。我们可以使用try…catch来取代.catch。同时这也将更为便利。
但处于顶层代码逻辑时,我们的逻辑代码处在async function以外,我们并不能直接使用await,所以添加.then/catch代码块来获取最终结果是更为普遍的做法。

async/await与Promise.all相互合作

当我们需要等待多个promises时,我们可以使用Promise.all之后使用await:

// wait for the array of results
let results = await Promise.all([
  fetch(url1),
  fetch(url2),
  ...
]);

假如触发了一个错误,它也会和其他promise一样工作:从运行失败的Promise.all节点中断,之后我们可以通过try…catch来捕获错误。

总结

async定以后的函数有两层作用:

确保它总是返回一个promise。

允许在函数内部使用await

await关键词确保js运行时将会等待promise处理完毕,并且:

如果触发了一个运行错误,promise运行中断,并在改处类似触发throw error

此外,它将返回一个结果,所以我们可以将其赋值给一个变量。

我们一起提供了一个伟大的框架来更为简便的完成异步操作。

有了async/await的帮组,我们可以大幅减少使用promise.then/catch,但我们依然不应该忘记这些技术是基于promises,很可能我们会不得不继续使用promise的方法。同时,Promise.all相当适合等待多个任务顺序执行的操作。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90046.html

相关文章

  • 现代JS中的流程控制:详解Callbacks 、PromisesAsync/Await

    摘要:控制台将显示回调地狱通常,回调只能由一个异步函数调用。更多资源使更友好规范使用异步函数简化异步编码旅程异步编程是一项在中无法避免的挑战。 JavaScript经常声称是_异步_。那是什么意思?它如何影响发展?近年来这种方法有何变化? 请思考以下代码: result1 = doSomething1(); result2 = doSomething2(result1); 大多数语言都处理每...

    shadowbook 评论0 收藏0
  • 现代JS中的流程控制:详解Callbacks 、PromisesAsync/Await

    摘要:控制台将显示回调地狱通常,回调只能由一个异步函数调用。更多资源使更友好规范使用异步函数简化异步编码旅程异步编程是一项在中无法避免的挑战。 JavaScript经常声称是_异步_。那是什么意思?它如何影响发展?近年来这种方法有何变化? 请思考以下代码: result1 = doSomething1(); result2 = doSomething2(result1); 大多数语言都处理每...

    oujie 评论0 收藏0
  • 现代JS中的流程控制:详解Callbacks 、PromisesAsync/Await

    摘要:控制台将显示回调地狱通常,回调只能由一个异步函数调用。更多资源使更友好规范使用异步函数简化异步编码旅程异步编程是一项在中无法避免的挑战。 JavaScript经常声称是_异步_。那是什么意思?它如何影响发展?近年来这种方法有何变化? 请思考以下代码: result1 = doSomething1(); result2 = doSomething2(result1); 大多数语言都处理每...

    anquan 评论0 收藏0
  • async & await (译)

    摘要:的出现,让我们可以走出回调地狱,着实惊艳。我已经开始使用里的和关键字来简化的处理。异步任务在这个例子是执行之后,一直在执行完成才继续下一个任务并没有产生阻塞。最后这个函数处理了返回值并且返回了一个对象。依然很棒,但和使得它可维护性更好。 JavaScript Promises的出现,让我们可以走出回调地狱,着实惊艳。Promises 允许我们更好的引入和处理异步任务,虽然如此,但引入好...

    The question 评论0 收藏0
  • 翻译:Taming the asynchronous beast with ES7

    摘要:让我们使用它从数组中返回一个值数组在中,我们可以这样做,这是一种更简单的方法最重要的部分是创建数组,该数组立即调用所有的我们在主函数中等待这些。所以在我们真正等待完成之前,主函数就退出了。 原文:https://pouchdb.com/2015/03/0... PouchDB最棘手的方面之一是它的API是异步的。在Stack Overflow、Github和IRC上,我看到了不少困惑的...

    Eastboat 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<