【问题标题】:Async function without await in JavaScriptJavaScript 中没有等待的异步函数
【发布时间】:2018-01-17 13:47:27
【问题描述】:

我有两个函数,ab,它们是异步的,前者不带 await,后者带 await。他们都在控制台上记录一些东西并返回undefined。在调用任一函数后,我会记录另一条消息并查看该消息是在执行函数体之前还是之后编写的。

function someMath() {
  for (let i = 0; i < 9000000; i++) { Math.sqrt(i**5) }
}

function timeout(n) {
   return new Promise(cb => setTimeout(cb, n))
}

// ------------------------------------------------- a (no await)
async function a() {
  someMath()
  console.log('in a (no await)')
}

// ---------------------------------------------------- b (await)
async function b() {
  await timeout(100)
  console.log('in b (await)')
}

clear.onclick = console.clear

aButton.onclick = function() {
  console.log('clicked on a button')
  a()
  console.log('after a (no await) call')
}

bButton.onclick = function() {
  console.log('clicked on b button')
  b()
  console.log('after b (await) call')
}
<button id="aButton">test without await (a)</button>
<button id="bButton">test with await (b)</button>
<button id="clear">clear console</button>

如果您在没有await 的情况下启动测试,则该功能似乎像同步一样工作。但是使用await,消息会反转,因为函数是异步执行的。

当没有await 关键字时,JavaScript 如何执行async 函数?


实际用例:我有一个有条件执行的await关键字,我需要知道该函数是否同步执行才能渲染我的元素:

async function initializeComponent(stuff) {
   if (stuff === undefined)
      stuff = await getStuff()
   // Initialize

   if (/* Context has been blocked */)
       renderComponent() // Render again if stuff had to be loaded
}

initializeComponent()
renderComponent()

P.S:标题带有 JavaScript 关键字以避免与其他语言中的相同问题混淆(即Using async without await

【问题讨论】:

  • 您还需要将bButton.onclick函数转换为异步函数并等待b()结束才能获得所需的日志。
  • @JoseHermosillaRodrigo 我不想等待所需的日志,我想知道使用或不使用 await 关键字会改变同步功能。如果没有,也许为什么我的测试是错误的。最后我会更新我的真实用例,也许对你来说会更清楚。

标签: javascript asynchronous async-await


【解决方案1】:

Mozilla documentation:

一个异步函数可以包含一个等待表达式,它会暂停 执行异步函数并等待传递的 Promise 解决,然后恢复异步函数的执行和 返回解析后的值。

正如您所假设的,如果没有等待,则不会暂停执行,然后您的代码将以非阻塞方式执行。

【讨论】:

  • 有多余的async关键字的eslint规则吗?我不认为同步函数应该有它
  • 哦,是的,就是这个。不知何故错过了:D谢谢
  • 在返回承诺但不使用await 的函数中声明“异步”的一个好处是异常将被拒绝而不是同步抛出。例如const result = someAsyncFunctionWithoutAwait().catch(handleRejection)
  • 换句话说,这个答案不应该说“如果没有等待,则执行不会暂停,但您的代码仍将以非阻塞方式执行”而不是“如果没有等待是否存在执行未暂停,然后您的代码将同步执行”?此外,如果不存在 async 和 await,您的代码仍将是非阻塞的(例如,使用 async 关键字声明一个函数而不使用 await 等同于不使用 async 关键字声明同一个函数)。
  • 查看它的一个好方法是 Async/Await 允许您同步异步代码:async function dummy(){ /*...*/ return Promise.resolve(/*...*/); } async dummyAsync(){ dummy(); nextDummy(); } async dummySync(){ await dummy(); nextDummy(); }。在 dummyAsync 中,nextDummy()dummy() 完成之前不会停止执行;它的异步。在 dummySync 中,nextDummy() 不会执行,直到来自 dummy() 的承诺解决(返回);它是同步,或程序编写异步代码。 @AdrianPop 完美地用于声明异步和不调用等待。
【解决方案2】:

在执行 JavaScript 异步函数之前,一切都是同步的。在使用 async-await 时,await 是异步的,await 之后的所有内容都放在事件队列中。类似于.then()

为了更好地解释,举个例子:

function main() {
  return new Promise( resolve => {
    console.log(3);
    resolve(4);
    console.log(5);
  });
}

async function f(){
    console.log(2);
    let r = await main();
    console.log(r);
}

console.log(1);
f();
console.log(6);

由于await是异步的,其余都是同步的,包括promise,所以输出是:

1
2
3
5
6
// Async happened, await for main()
4

main() 的类似行为也没有承诺:

function main() {
    console.log(3);
    return 4;
}

async function f(){
    console.log(2);
    let r = await main();
    console.log(r);
}

console.log(1);
f();
console.log(5);

输出:

1
2
3
5
// Asynchronous happened, await for main()
4

仅删除 await 将使整个 async 函数同步。

function main() {
    console.log(3);
    return 4;
}

async function f(){
    console.log(2);
    let r = main();
    console.log(r);
}

console.log(1);
f();
console.log(5);

输出:

1
2
3
4
5

【讨论】:

  • 您还可以添加一个执行 var g = await f() 的函数,因为 f() 是异步的,如果您不想阻止主要执行,可能需要在长时间运行的进程中等待线程。
  • await 只能在async 函数内部使用。
  • 请注意这是 ES11 之前的旧实现。在 ES11 中,事情发生了一点变化。但这仍然有效。
  • 事件队列中的代码(await之后的所有内容)是否总是在主线程完成后运行?我的意思是,在您的两个示例中,在您最后一个 console.log 之后,事件队列运行,并且正在“等待”的代码最终执行。如果我们有更多的指令和更多的代码,在最后一条指令之后,等待的代码是否仍然会运行?
【解决方案3】:

无论有没有await,函数的执行方式都是一样的。 await 所做的是自动等待函数返回的 promise 被解析。

await timeout(1000);
more code here;

大致相当于:

timeout(1000).then(function() {
    more code here;
});

async function 声明只是让函数自动返回一个在函数返回时解析的承诺。

【讨论】:

【解决方案4】:

正如其他答案所说/表明:async function 只是在现场运行,直到遇到await - 如果没有await,它会完全运行。

值得补充的是async 无条件地使您的结果为Promise。所以如果你返回一些东西,已经有区别了,如果不先返回 JS 引擎,你根本无法得到结果(类似于事件处理):

async function four(){
  console.log("  I am four");
  return 4;
}
console.log(1);
let result=four();
console.log(2,"It is not four:",result,"Is it a promise ?", result instanceof Promise);
result.then(function(x){console.log(x,"(from then)");});
console.log(3);

【讨论】:

  • @UlysseBN 我想我找到了中间地带:-)。虽然这里出现的{}不是很壮观,但它已经表明它肯定不是4,值得一提的是,在浏览器的JS控制台中可以看到并查看实际对象。
  • 是的,我猜这就是你所期望的!不幸的是,我不知道在 JS 中显示实例来自的类的好方法......(但instanceof
猜你喜欢
  • 1970-01-01
  • 2022-01-01
  • 2011-03-04
  • 1970-01-01
  • 1970-01-01
  • 2022-07-06
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
相关资源
最近更新 更多