34

我对当前关于将异步函数和关键字添加await到下一个 EcmaScript 的讨论感到困惑。

我不明白为什么必须在async关键字之前有function关键字。

从我的角度来看,await关键字等待生成器或承诺完成的结果,一个函数return就足够了。

await应该可以简单地在普通函数和生成器函数中使用,而无需额外的async标记。

如果我需要创建一个结果应该可以用于 an 的函数await,我只需使用一个 Promise。

我问的原因是这个很好的解释,以下示例来自:

async function setupNewUser(name) {  
  var invitations,
      newUser = await createUser(name),
      friends = await getFacebookFriends(name);

  if (friends) {
    invitations = await inviteFacebookFriends(friends);
  }

  // some more logic
}

如果函数的执行将等待完成空洞函数,直到所有等待都完成,它也可以作为普通函数完成。

function setupNewUser(name) {  
  var invitations,
      newUser = await createUser(name),
      friends = await getFacebookFriends(name);

  if (friends) {
    invitations = await inviteFacebookFriends(friends);
  }

  // return because createUser() and getFacebookFriends() and maybe inviteFacebookFriends() finished their awaited result.

}

在我看来,整个函数的执行一直持续到下一个滴答(等待履行)完成。与 Generator-Function 的区别在于 next() 是触发和更改对象的 value 和 done 字段。相反,函数将在完成后简单地返回结果,并且触发器是函数内部触发器,如 while 循环。

4

4 回答 4

22

我不明白为什么必须async在 function 关键字之前有关键字。

出于同样的原因,我们*在生成器函数之前有符号:它们将函数标记为非凡。它们在这方面非常相似——它们添加了一个视觉标记,表明此函数的主体不会自行运行完成,但可以与其他代码任意交错。

  • 表示一个生成器函数,它将始终返回一个生成器,该*生成器可以通过使用类似于迭代器的方式从外部推进(和停止)。
  • async表示一个异步函数,它将始终返回一个依赖于其他承诺的承诺,并且其执行与其他异步操作并发(并且可能从外部取消)。

确实,关键字不是绝对必要的,函数的类型可以通过相应的关键字 ( yield(*)/ await) 是否出现在其主体中来确定,但这会导致代码的可维护性降低:

  • 不太容易理解,因为你需要扫描全身才能确定种类
  • 更容易出错,因为通过添加/删除这些关键字很容易破坏函数而不会出现语法错误

一个普通函数,其执行将等待完成孔体,直到所有等待都完成

这听起来像你想要一个阻塞函数,这在并发设置中是一个非常糟糕的主意

于 2015-07-17T21:59:28.093 回答
19

通过将函数标记为async,您是在告诉 JS 始终返回一个 Promise。

因为它总是会返回一个 Promise,所以它也可以在它自己的块内等待Promise。把它想象成一个巨大的 Promise 链——函数内部发生的事情被有效地固定在它的内部.then()块上,返回的是.then()链中的最后一个。

比如这个函数...

async function test() {
  return 'hello world';
}

...返回一个承诺。因此,您可以像一个整体一样执行它.then()

test().then(message => {
  // message = 'hello world'
});

所以...

async function test() {
  const user = await getUser();
  const report = await user.getReport();
  report.read = true
  return report;
}

大致类似于...

function test() {
  return getUser().then(function (user) {
    return user.getReport().then(function (report) {
      report.read = true;
      return report;
    });
  });
}

在这两种情况下,传递给的回调都test().then()report作为其第一个参数接收。

生成器(即标记函数*并使用yield关键字)完全是一个不同的概念。他们不使用承诺。它们有效地允许您在代码的不同部分之间“跳转”,从函数内部产生结果,然后跳回到该点并恢复下一个 yield 块。

尽管它们感觉有些相似(即“暂停”执行,直到其他地方发生某些事情),async/await但只会给您这种错觉,因为它与 Promise 执行的内部顺序相混淆。它实际上并不是在等待——它只是在回调发生时随机播放。

相比之下,生成器的实现方式不同,因此生成器可以保持状态并进行迭代。同样,与 Promises 无关。

这条线更加模糊,因为在撰写本文时,对 async/await 的支持还很可怕;Chakracore 原生支持,V8 即将推出。同时,像 Babel 这样的编译器允许你编写async/await代码并将其转换为生成器。因此得出生成器和 async/await 相同的结论是错误的;他们不是......碰巧你可以混蛋如何yield与 Promises 一起工作以获得类似的结果。

更新:2017 年 11 月

Node LTS 现在有原生async/await支持,所以你应该永远不需要使用生成器来模拟 Promise。

于 2016-09-08T06:40:43.963 回答
10

这些答案都为为什么 async 关键字是一件好事提供了有效的论据,但没有一个真正提到必须将其添加到规范中的真正原因。

原因是这是一个有效的 JS pre-ES7

function await(x) {
  return 'awaiting ' + x
}

function foo() {
  return(await(42))
}

根据您的逻辑,将foo()返回Promise{42}"awaiting 42"?(返回 Promise 会破坏向后兼容性)

所以答案是:await是一个常规标识符,它仅被视为异步函数中的关键字,因此必须以某种方式对其进行标记。

有趣的事实:原始规范function^ foo() {}为异步语法提出了更轻量级的建议。

于 2017-01-19T14:25:29.690 回答
3

前面使用 async 关键字的原因很简单,因此您知道返回值将转换为 Promise。如果没有关键字,解释器将如何知道这样做。我认为这是在 C# 中首次引入的,而 EcmaScript 正在从 TypeScript 中获取大量内容。TypeScript 和 C# 由 Anders Hejlsberg 构想并且相似。假设您有一个功能(这个功能只是为了进行一些异步工作)

 function timeoutPromise() {  
     return (new Promise(function(resolve, reject) {
         var random = Math.random()*1000;
         setTimeout(
             function() {
                 resolve(random);
             }, random);
     }));
 }

这个函数会让我们等待一个随机时间并返回一个 Promise(如果你使用 jQuery Promise 类似于 Deferred)对象。今天要使用这个函数,你会写这样的东西

function test(){
    timeoutPromise().then(function(waited){
        console.log('I waited' + waited);
    });
}

这很好。现在让我们尝试返回日志消息

function test(){
    return timeoutPromise().then(function(waited){
        var message = 'I waited' + waited;
        console.log(message);
        return message; //this is where jQuery Deferred is different then a Promise and better in my opinion
    });
}

好的,这还不错,但是代码中有两个返回语句和一个函数。

现在使用 async 这将看起来像这样

  async function test(){
      var message = 'I waited' +  (await timeoutPromise());
      console.log(message);
      return message;
  }

代码简短且内联。如果你写了很多 .then() 或 . done() 你知道代码有多不可读。

现在为什么要在函数前面加上 async 关键字。好吧,这是为了表明您的返回值不是返回的值。理论上你可以写这个(这可以在c#中完成,我不知道js是否允许,因为它没有完成)。

 async function test(wait){
     if(wait == true){
         return await timeoutPromise();
     }
     return 5;                
 }

您会看到,您返回一个数字,但实际返回将是您不必使用的 Promise return new Promise(function(resolve, reject) { resolve(5);}; 因为您不能等待数字,所以只有 Promiseawait test(false)会抛出异常,await test(true)如果您没有在前面指示 async 则不会。

于 2016-06-09T00:50:33.950 回答