如何等待异步函数?

How to await an asynchronous function?

我的情况:

let waiting = function () {
  return new Promise(resolve => {
    console.log('awaiting...');
    
    setTimeout(function () {
      resolve();
    }, 1000)
  });
};

let waitingAsync = async function () {
  console.log('start...');

  await waiting();

  console.log('stop...');
};

waitingAsync();
console.log('done...');

代码中有两处我不明白:

第一个:

await waiting();

waiting 是一个同步函数(因为它没有 async 关键字)。那么,为什么我可以等待一个同步函数?

第二个:

为什么在完成 waitingAsync 功能后不能等待 done... 消息?

还有主要问题:waitingAsync是一个异步函数,为什么调用时不需要await关键字?只是 waitingAsync() 而不是 await waitingAsync().

如果我可以等待 waitingAsync()done... 消息将最后打印。

Async 关键字用于指定该函数将是 AsyncFunction 的实例,因此它将 return Promise.

Await 用于等待异步函数内部的 promise 解析。


According to a mdn - 异步函数可以包含一个 await 表达式,暂停异步函数的执行并等待传递的承诺的解析,然后恢复异步函数的执行和 returns 解析值。

这不是一个函数,而是一个值,它 return 正在等待 await 语句。

async 和普通函数对于调用者来说没有什么不同。 async 只是 return 一个承诺,在被调用时没有 return 显式地调用它。 waitingAsync() 调用的结果是一个承诺。 waiting() 调用的结果也是一个承诺,所以它不是 'synchronous'。

根据 the spec,promise 和 non-promises 都可以 awaited。 Non-promises 转换为 Promise.resolve() 的承诺。

console.log('done...') 无法等待,因为它未在 async 函数内部调用。而且它不必 awaited 因为它不是 return 承诺而是 undefinedawaiting 在 async 函数中是可能的。这些 await 用法是一样的,同样没有用,它们所做的只是 1 个刻度延迟:

async function ... {
  ...
  await console.log('done...');
}

async function ... {
  ...
  console.log('done...');
  await undefined;
}

async function ... {
  ...
  await Promise.resolve(console.log('done...'));
}

一个async函数做return一个promise,或者当它使用await关键字时,它必须等待一个异步函数,一个Promise或另一个async 功能。在您的代码中,waiting() 是一个 return 是 Promise 的函数。那么,await waiting() 是正确的(因为它正在等待异步函数)。

一个async函数被调用为另一个基本函数被调用。当您希望将函数作为异步函数运行并使用 'await' 关键字时,您可以将函数标记为异步。打印 'done...' 是因为当您调用 asyncFunction() 时,它必须 await 直到 waiting promise 完成。但是程序没有停止,继续显示done...。如果你想等待,也许你可以使用asyncFunction().then( () => console.log('done...') )

当您 await 函数时,如果该函数 return 是一个承诺,其 return 值将被视为承诺 then 值。如果承诺将被拒绝,它将被转换为一个错误。如果函数调用 return 比 thenable 更重要,那么 await 就什么都不是。

另一方面,当您声明一个 async 函数时,它的 return 值将被 return 编辑为 Promise,并且从中抛出的任何错误都将被转换为一个被拒绝的承诺。

您只能在 async 声明的函数中使用 await

差不多 asyncawait 是,只是自动转换为 promise。您实际上并不需要使用 await 和 async 的代码才能真正异步(虽然,它并不是很有用)。

快速演示:

//Will return the string 'Promise' if called through `await`
function getPromise(){
    return Promise.resolve('Promise');
}

//Casted to Promise.reject thrught await 
function throwError(){
    return Promise.reject('error'); 
}

function get(){
    return 'something simple'; 
}

async function getAsync() {
    var response = await getPromise();
    return response;
}
//await will cast the rejected Promise to an error
async function getErrorAsync() {
    var response = await throwError();
    return response;
}

async function simpleGet(){
    return get();
}

async function redundantGet(){
    return await get();
}

    async function catchTheError(){
      try{
          await throwError();
      }       
      catch(e){
         console.log('an error: ' + e );
      }
       return 'whatever';
 }

getAsync().then( console.log ); //Promise
getErrorAsync().catch( console.log ); //error
simpleGet().then( console.log ); //something simple
redundantGet().then( console.log ); //something simple
catchTheError(); //will log 'an error: error'.

所以:

waiting is a synchronous function (because it doesn't have async keyword). So, why can I await a synchronous function?

因为你可以。 await 所做的唯一一件事就是解决对实际值和错误的承诺。您实际上并不需要 return 承诺的功能。

Why couldn't done... message be awaited after completing waitingAsync function?

asyncawait 只会使您的代码表现得像在 async 声明的函数中同步一样。您的最后一个 console.log('done') 在任何 async 函数之外,因此它将在该函数结束之前被记录下来,因为它是异步的。

And main question: waitingAsync is an asynchronous function, why is await keyword not required when calling it? Just waitingAsync() instead of await waitingAsync().

因为 async 关键字将值转换为承诺 - 并允许使用 await- 仅此而已。事实上,由于您只能在 async 函数中使用 await...您不能指望通过 await 调用 async 函数,您需要无限的 async函数:-D.

在开始之前,最好注意一些事情。
任意reader的代码片段

let waiting = function () {
  return new Promise(resolve => {
    console.log('awaiting...');
    setTimeout(function () { resolve(); }, 1000);
  });
};

let waitingAsync = async function () {
  console.log('start...');
  await waiting();
  console.log('stop...');
};

waitingAsync();
console.log('done...');

可能会误以为输出会是

start...
awaiting...
stop...
done...

而 – 正如您已经注意到的 – done... 之前 被打印 stop....

原因是waitingAsync();是对异步函数的调用, 而 console.log('done...'); 只是一个普通的 sequential/synchronous 立即执行的声明。


问题 1:

waiting is a synchronous function (because it doesn't have async keyword) [?]

回答:
False.函数waitinga同步它returns 一个承诺.


问题2:

Why couldn't done... message be awaited after completing waitingAsync function?

回答:
因为console.log('done...')不是异步的。
(它 不是 return Promise。)


问题 3:

And main question: waitingAsync is an asynchronous function, why is await keyword not required when calling it?

回答:
那么,在您的示例中 waitingAsync 没有 return 任何值。 - 如果它 return 是您关心的值,那么您需要等待 它得到它。
Hello world! 在我下面的 Stack Snippet 中。)


问题4:

If I can await waitingAsync(), [the] done... message would be printed last [?]

回答:
这取决于你的意思。 – 请参阅下面我的 Stack Snippet! 只要 Done! 消息在与调用相同的回调中打印 await waitingAsync(),答案是是!
但是如果你把 console.log('done...?') 放在 之后调用异步 包含 await waitingAsync() 的函数那么答案是 No!


当运行下面的代码片段时,注意输出的顺序
另请注意 Promise resolved! 需要 1400 ms 才能显示。

function waiting () {
  return new Promise(resolve => {
    console.log('awaiting...');
    setTimeout(function () {
      resolve('Hello world!');
      console.log('Promise resolved!');
    }, 1400);
  });
}

async function waitingAsync () {
  console.log('start...');
  const toBeReturned = await waiting();
  console.log('stop...');
  return toBeReturned;
}

(async () => {
  console.log('Heads up! The next line makes an asynchronous call.');
  console.log('Result: ' + await waitingAsync());  // 'Hello world!'
  console.log('Done! This will be printed LAST! - Agreed?');
})();

console.log('done...?? This is LAST in the CODE. - I awaited \
"waitingAsync()" above. - So will this be printed at the very end??');
.as-console-wrapper { max-height: 100% !important; top: 0; }

最后一个异步函数是匿名的——没有名字——并且得到 立即打电话。 事实上,这是代码段中唯一 直接 调用的函数。 函数 waitingAsync 仅在 中被直接调用 (由匿名 函数),并且函数 waiting 也被称为 间接 (作者 waitingAsync)。

外带课

永远不要在 sequential/synchronous 代码 之后和外部 调用 一个异步函数!

如果你这样做,你只会把自己弄糊涂。 – 即使 不明白 很困惑,other readers 你的代码几乎肯定会。