如何等待异步函数?

4

我的案例:

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...消息将会最后打印。


2
“async”并不意味着函数的返回值可以使用“await”进行等待。只有当函数返回一个“Promise”时,才能使用“await”等待它,就像你的例子中的“waiting”一样。但是,“async”对于函数是必需的,就像在“waitingAsync”函数上一样,以启用其中的“await”使用。 - Jesper
2
此外,waitingAsync 函数的返回值会自动转换为 Promise,因为它不能再提供同步结果。因此,现在可以使用 await 等待它的执行结果。但这并不是函数能够被等待的唯一方式,否则你将无法编写第一个这样的函数。 - Jesper
async”并不意味着函数的返回值可以使用“await”等待。错误! 每个“async”函数都返回一个Promise对象。 在这种情况下唯一的注意点是“waiting”函数返回的Promise无法评估到任何值(void),因为该行代码中存在resolve(); - Henke
5个回答

6
这不是一个函数,而是返回值需要用await语句来等待的值。
对于调用者来说,async和普通函数没有区别。 async在被调用时会自动返回一个Promise而无需显式地返回它。调用waitingAsync()的结果是一个Promise。调用waiting()的结果也是一个Promise,因此它不是“同步”的。
根据规范,Promises和非-Promises都可以被await。使用Promise.resolve()将非-Promises转换为Promises。 console.log('done...')不能使用await,因为它不是在async函数内部调用的。而且它不必使用await,因为它返回的是undefined而不是Promise。在async函数内部,应该可以使用await。这些await用法是相等和同样无用的,它们只会导致一次时间延迟。
async function ... {
  ...
  await console.log('done...');
}

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

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

3
async函数会返回一个promise,或者在使用await关键字时,必须等待异步函数完成,这个异步函数可以是一个Promise或另一个async函数。在您的代码中,waiting()是一个返回Promise的函数。因此,await waiting()是正确的(因为它正在等待一个异步函数)。
一个async函数像调用其他基本函数一样被调用。当您希望将其作为异步函数进行操作并使用'await'关键字时,可以将函数标记为async。之所以打印出“done...”,是因为当您调用asyncFunction()时,它必须等待直到waiting promise完成。但程序没有停止并继续显示done...。如果您想等待它,也许可以使用asyncFunction().then( () => console.log('done...') )

一个异步函数必须返回一个 Promise。它确实会返回一个 Promise。 - a better oliver

2

Async关键字用于指定函数将是AsyncFunction实例,因此它将返回Promise

Await用于在异步函数内等待解决的承诺。


根据MDN上的介绍 - 异步函数可以包含await表达式,该表达式会暂停异步函数的执行,并等待传递的承诺被解决,然后恢复异步函数的执行并返回解决的值。


2
当你使用await等待一个函数时,如果该函数返回一个Promise,它的返回值将被视为Promise的then值。如果Promise被拒绝,它将被转换为Error。如果函数调用返回的不是thenable,那么await就什么也不做。
另一方面,当你声明一个async函数时,它的返回值将作为Promise返回,任何从中抛出的错误都将转换为已拒绝的Promise。
只能在已声明为async的函数内部使用await
这就是关于asyncawait的全部内容,它们只是自动转换为Promises。实际上,你并不需要使用awaitasync的代码真正异步(虽然这并没有什么用)。
一个快速演示:
//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是一个同步函数(因为它没有async关键字)。那么,我为什么可以等待同步函数?

因为你可以。 await 唯一的作用就是将promise解析为实际值和错误。你并不需要函数返回promise。

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

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

最重要的问题:waitingAsync是一个异步函数,为什么在调用它时不需要 await 关键字?只需 waitingAsync() 而不是 await waitingAsync()。

因为 async 关键字将值转换为 promise - 并允许使用 await - 没有其他作用。实际上,由于您只能在 async 函数中使用 await ... 您不能指望通过 await 调用 async 函数,否则你就需要无限的 async 函数 :-D。


1
在深入了解之前,值得注意几件事情。
任何代码片段的读者。
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...');只是一个普通的顺序/同步语句,会立即执行。


问题 1:

waiting 是一个 同步 函数(因为它没有 async 关键字)[?]

答案:
错误。 函数 waiting异步 的 - 它返回一个 Promise。


问题2:

在完成waitingAsync函数后,为什么无法等待done...消息?

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


问题3::

主要问题是:waitingAsync是一个异步函数,为什么在调用它时不需要使用await关键字?

答案:
嗯,在你的例子中,waitingAsync没有返回任何值。- 如果它返回了你关心的值,那么你需要等待它才能得到它。
(Hello world! 在下面的我的Stack Snippet中。)


问题4:

如果我可以等待waitingAsync(), 那么done...信息会最后被打印出来吗?

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


运行下面的代码片段时,请注意输出的顺序!
还要注意需要等待1400ms才会显示“Promise resolved!”的信息。

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)。
一个需要记住的教训 永远不要在异步函数的调用之后和外面放置顺序/同步代码! 如果你这么做,你会把自己搞糊涂。 - 即使你不会感到困惑,你的代码的其他读者几乎肯定会感到困惑。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接