在JavaScript中,在循环中使用`await`阻塞循环吗?

时间:2017-06-07 10:29:58

标签: javascript async-await ecmascript-next

采取以下循环:

for(var i=0; i<100; ++i){
    let result = await some_slow_async_function();
    do_something_with_result();
}
  1. await阻止循环吗?或者iawait继续递增?

  2. 关于do_something_with_result()i的顺序是否有序保证?或者它取决于每个await的{​​{1}} ed函数的速度有多快?

8 个答案:

答案 0 :(得分:39)

  
      
  1. await阻止循环吗?或者在i ing?
  2. await是否会继续增加?   

“阻止”不是正确的词,但是, i 在等待时不会继续增加。相反,执行会跳回到调用async函数的位置,提供一个promise作为返回值,继续执行函数调用之后的其余代码,直到代码堆栈被清空为止。然后当等待结束时,恢复功能的状态,并在该功能内继续执行。只要该函数返回(完成),就会解决之前返回的相应承诺。

  
      
  1. do_something_with_result()的顺序是i保证顺序吗?或者它取决于每个await的{​​{1}} ed函数的速度有多快?
  2.   

订单有保证。 i之后的代码也保证仅在调用堆栈清空后才执行,即至少在下一个微任务执行时或之后执行。

查看此代码段中的输出结果。请特别注意“调用测试后”的位置:

await

答案 1 :(得分:9)

正如@realbart所说,它会阻止循环,然后循环调用。

如果您想要触发大量等待操作然后再一起处理它们,您可以执行以下操作:

document.onDomReady = function(){
    adShow = 0;
    document.onmousemove = function(){
        if (adShow == 0) {
                var leaderboard = CODE_FOR_AD;
                var adLeaderboard = document.querySelector('.adspace-leaderboard#adspace');
                adLeaderboard.innerHTML = leaderboard;
                adShow = 1;             
        }
    }
}

答案 2 :(得分:6)

您可以在“FOR LOOP”中测试async / await,如下所示:

(async  () => {
        for (let i = 0; i < 100; i++) {
                await delay();
                console.log(i);
        }
})();

function delay() {
        return new Promise((resolve, reject) => {
                setTimeout(resolve, 100);
        });
}

答案 3 :(得分:0)

没有阻止任何事件循环,请参见下面的示例

array (
  'input' => '312',
  'solutions' => 
  array (
    0 => 
    array (
      'sequence' => '222',
      'steps' => 2,
    ),
  ),
)
array (
  'input' => '334',
  'solutions' => 
  array (
    0 => 
    array (
      'sequence' => '333',
      'steps' => 1,
    ),
  ),
)
array (
  'input' => '39',
  'solutions' => 
  array (
    0 => 
    array (
      'sequence' => '33',
      'steps' => 6,
    ),
    1 => 
    array (
      'sequence' => '99',
      'steps' => 6,
    ),
  ),
)

答案 4 :(得分:0)

async函数返回一个Promise,该对象最终将“解析”为一个值,或因错误而“拒绝”。 await关键字表示要等到该值(或错误)完成后才能使用。

因此,从运行功能的角度来看,它阻止等待慢速异步功能的结果。另一方面,javascript引擎会发现此功能已被阻止等待结果,因此它将检查事件循环(例如,单击新鼠标或连接请求等),以查看是否还有其他情况它可以继续使用直到返回结果。

但是请注意,如果慢速异步功能很慢,因为它正在您的javascript代码中计算很多东西,则javascript引擎将没有很多资源来做其他事情(而做其他事情很可能会使慢速异步功能,甚至更慢)。异步功能真正发挥作用的地方在于进行I / O密集型操作,例如查询数据库或传输大文件,而javascript引擎运行良好,并真正在等待其他内容(例如数据库,文件系统等)。

以下两个代码在功能上是等效的:

let result = await some_slow_async_function();

let promise = some_slow_async_function(); // start the slow async function
// you could do other stuff here while the slow async function is running
let result = await promise; // wait for the final value from the slow async function

在上面的第二个示例中,慢速异步函数在没有await关键字的情况下被调用,因此它将开始执行该函数并返回一个Promise。然后,您可以做其他事情(如果有其他事情要做)。然后使用await关键字进行阻止,直到承诺实际上“解决”为止。因此,从for循环的角度来看,它将同步运行。

所以:

  1. 是的,await关键字具有阻止正在运行的功能的作用,直到异步功能“解析”一个值或“拒绝”一个错误,但是它不会阻止javascript引擎,如果它在等待时还有其他事情要做,它仍然可以做其他事情

  2. 是的,循环的执行将是连续的

http://javascript.info/async上有一个很棒的教程。

答案 5 :(得分:0)

ad 1.是。否

ad 2.是。不。

证明:

async function start() 
{
  console.log('Start');

  for (var i = 0; i < 10; ++i) {
    let result = await some_slow_async_function(i);
    do_something_with_result(i);
  }
  
  console.log('Finish');
}

function some_slow_async_function(n) {
  return new Promise(res => setTimeout(()=>{
    console.log(`Job done: ${(2000/(1+n))|0} ms`);
    res(true);
  },2000/(1+n)));
}

function do_something_with_result(n) {
  console.log(`Something ${n}`);
}

start();

答案 6 :(得分:0)

这是我针对这个有趣问题的测试解决方案:

import crypto from "crypto";

function diyCrypto() {
    return new Promise((resolve, reject) => {
        crypto.pbkdf2('secret', 'salt', 2000000, 64, 'sha512', (err, res) => {
            if (err) {
                reject(err)
                return 
            }
            resolve(res.toString("base64"))
        })
    })
}

setTimeout(async () => {
    console.log("before await...")
    const a = await diyCrypto();
    console.log("after await...", a)
}, 0);

setInterval(() => {
    console.log("test....")
}, 200);

在setTimeout的回调中,await阻止了执行。但是setInterval一直在运行,因此事件循环像往常一样运行。

答案 7 :(得分:-2)

  

等待阻塞循环吗?还是i在等待期间继续增加?

否,await不会阻止循环。是的,i在循环时继续增加。

  

关于i的do_something_with_result()的顺序是否保证是连续的?还是取决于每个i的等待功能有多快?

依次保证do_something_with_result()的顺序,但不能保证i 的顺序。这取决于等待的功能运行的速度。

所有对some_slow_async_function()的调用都被分批处理,即,如果do_something_with_result()console,那么我们将看到它显示了循环运行的次数。然后依次执行,之后将执行所有的等待调用。

为更好地了解,您可以在以下代码段中运行:

async function someFunction(){
for (let i=0;i<5;i++){
 await callAPI();
 console.log('After', i, 'th API call');
}
console.log("All API got executed");
}

function callAPI(){
setTimeout(()=>{
console.log("I was called at: "+new Date().getTime())}, 1000);
}

someFunction();

一个人可以清楚地看到在整个for循环中如何首先打印行console.log('After', i, 'th API call');,然后在执行完所有代码后从callAPI()获取结果。

因此,如果等待后的行取决于从等待调用获得的结果,则它们将无法按预期工作。

最后,await中的for-loop不能确保成功完成从 等待调用 获得的结果的操作,这可能需要一些时间才能完成。

在节点中,如果将neo-async库与waterfall一起使用,则可以实现这一目标。