ES2017 - 异步与收益率

时间:2015-07-17 19:28:42

标签: javascript ecmascript-6 async-await ecmascript-2017

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

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

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

await应该可以在普通函数和生成函数中使用,而不需要额外的async标记。

如果我需要创建一个函数作为await的结果,我只需使用一个承诺。

我问的理由是this很好的解释,以下示例来自:

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()正在触发并更改对象的值和完成字段。一个函数将简单地返回结果,并且触发器是一个函数内部触发器,如while循环。

4 个答案:

答案 0 :(得分:17)

  

我不明白为什么在函数关键字之前必须有echo "select id,concat('/opt/zimbra/store/', (mailbox_id >> 12), '/', mailbox_id, '/msg/',(id % (1024*1024) >> 12), '/', id, '-', mod_content, '.msg') as file, metadata from mail_item where mailbox_id=5 and id > 200 and id < 300"|mysql mboxgroup5 关键字。

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

  • *表示一个生成器函数,它总是会返回一个可以从外部推进(和停止)的生成器,类似于迭代器。
  • *表示一个异步函数,它将始终返回一个依赖于其他promises的promise,其执行与其他异步操作并发(并且可能从外部取消)。

关键字不是绝对必要的,而且函数的类型可以通过相应的关键字(async / yield(*))是否出现在其正文中来确定,但这会导致更少可维护的代码:

  • 不易理解,因为你需要扫描整个身体来确定那种
  • 更多errorprone,因为通过添加/删除这些关键字而不会出现语法错误很容易破解功能
  

正常函数,执行将等待完成孔体直到满足所有等待

听起来你想要一个阻塞函数,它在并发设置中是very bad idea

答案 1 :(得分:13)

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

因为它总是返回一个Promise,它也可以等待在它自己的块内部的promises上。想象一下它就像一个巨大的Promise链 - 函数内部发生的事情会被有效地固定到其内部.then()块上,返回的是链中的最终.then()

例如,这个功能......

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

...返回一个Promise。所以你可以像.then()和所有。{/ p>那样执行它

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关键字)完全是一个不同的概念。他们不使用Promises。它们有效地允许您在代码的不同部分之间“跳转”,从函数内部生成结果,然后跳回到该点并恢复到下一个yield块。

虽然他们觉得有些相似(即'停止'执行,直到其他地方发生了某些事情),但async/await只会给你一种错觉,因为它会与Promise执行的内部排序混淆。它不是实际等待 - 它只是在回调发生时改组。

相比之下,生成器的实现方式不同,因此生成器可以维护状态并进行迭代。再一次,与Promises无关。

这条线路进一步模糊,因为在当前写作时,对async / await的支持很吓人; Chakracore原生支持它,V8 has it coming soon。与此同时,像Babel这样的转发器允许您编写async/await并将代码转换为generators。结论发电机和异步/等待是相同的是错误的;他们不是......只是碰巧你可以将yield和Promises一起工作以获得类似的结果。

更新:2017年11月

节点LTS现在具有本机async/await支持,因此您永远不需要使用生成器来模拟Promises。

答案 2 :(得分:6)

这些答案都给出了为什么async关键字是好事的有效论据,但它们都没有提到将 添加到规范的真正原因。

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

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

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

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

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

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

答案 3 :(得分:2)

前面的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
    });
}

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

现在使用异步,这将是这样的

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

你看,你返回一个数字,但实际的回报将是你不必使用的承诺 return new Promise(function(resolve, reject) { resolve(5);}; 由于您无法等待一个号码,因此只有承诺await test(false)会抛出异常而await test(true)如果您没有在前面表示异步,则不会。