В JavaScript блокирует ли использование await внутри цикла?

Возьмем следующий цикл:

for(var i=0; i<100; ++i){
    let result = await some_slow_async_function();
    do_something_with_result();
}
  1. await ли блокирование цикла? Или i продолжаю увеличиваться в await?

  2. Является ли порядок do_something_with_result() гарантированным последовательным по отношению к i? Или это зависит от того, насколько быстро функция await будет для каждого i?

Ответ 1

  1. await ли блокирование цикла? Или i продолжаю увеличиваться в await?

"Блокировать" - это не правильное слово, но да, я не буду продолжать увеличиваться при ожидании. Вместо этого выполнение переходит обратно туда, где была вызвана функция async, предоставляя обещание в качестве возвращаемого значения, продолжая остаток кода, который следует после вызова функции, до тех пор, пока стек кода не будет опустошен. Затем, когда ожидание завершено, состояние функции восстанавливается, и выполнение продолжается в пределах этой функции. Всякий раз, когда эта функция возвращает (завершает), соответствующее обещание, которое было возвращено ранее, разрешено.

  1. Является ли порядок do_something_with_result() гарантированным последовательным по отношению к i? Или это зависит от того, насколько быстро функция await будет для каждого i?

Заказ гарантирован. Код, следующий за await, также гарантированно выполняется только после того, как стек вызовов был опустошен, то есть по крайней мере на или после того, как следующая микрозадача может выполнить.

Посмотрите, как вывод находится в этом фрагменте. Обратите внимание, особенно там, где говорится "после вызова теста":

async function test() {
    for (let i = 0; i < 2; i++) {
        console.log('Before await for ', i);
        let result = await Promise.resolve(i);
        console.log('After await. Value is ', result);
    }
}

test().then(_ => console.log('After test() resolved'));

console.log('After calling test');

Ответ 2

Как говорит @realbart, он блокирует цикл, который затем сделает вызовы последовательными.

Если вы хотите вызвать тонну ожидаемых операций и затем обрабатывать их все вместе, вы можете сделать что-то вроде этого:

const promisesToAwait = [];
for (let i = 0; i < 100; i++) {
  promisesToAwait.push(fetchDataForId(i));
}
const responses = await Promise.all(promisesToAwait);

Ответ 3

Вы можете проверить async/wait внутри "FOR LOOP" следующим образом:

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

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

Ответ 4

Ни один цикл событий не заблокирован, см. Пример ниже

function sayHelloAfterSomeTime (ms) {
  return new Promise((resolve, reject) => {
    if (typeof ms !== 'number') return reject('ms must be a number')
    setTimeout(() => { 
      console.log('Hello after '+ ms / 1000 + ' second(s)')
      resolve()  
    }, ms)
  })
}

async function awaitGo (ms) {
   await sayHelloAfterSomeTime(ms).catch(e => console.log(e))
   console.log('after awaiting for saying Hello, i can do another things  ...')
}

function notAwaitGo (ms) {
	sayHelloAfterSomeTime(ms).catch(e => console.log(e))
    console.log('i dont wait for saying Hello ...')
}

awaitGo(1000)
notAwaitGo(1000)
console.log('coucou i am event loop and i am not blocked ...')

Ответ 5

async функции возвращают Promise, который является объектом, который в конечном итоге "разрешается" до значения или "отклоняет" с ошибкой. Ключевое слово await означает ожидание, пока это значение (или ошибка) не будет завершено.

Таким образом, с точки зрения работающей функции, она блокирует ожидание результата медленной асинхронной функции. Механизм javascript, с другой стороны, видит, что эта функция заблокирована в ожидании результата, поэтому он проверит цикл событий (т.е. Новые щелчки мыши или запросы на подключение и т.д.), Чтобы увидеть, есть ли какие-либо другие вещи. он может работать до тех пор, пока не будут возвращены результаты.

Однако обратите внимание, что если медленная асинхронная функция медленная, потому что она вычисляет много вещей в вашем коде javascript, у движка javascript не будет много ресурсов, чтобы делать другие вещи (и выполнение других вещей, вероятно, сделает медленную асинхронную функцию еще медленнее). Где преимущество асинхронных функций действительно выгодно для интенсивных операций ввода-вывода, таких как запрос к базе данных или передача большого файла, где движок 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, поэтому она запускает выполнение функции и возвращает обещание. Тогда вы можете делать другие вещи (если у вас есть другие дела). Затем ключевое слово await используется для блокировки, пока обещание фактически не "разрешится". Таким образом, с точки зрения цикла for он будет работать синхронно.

Так:

  1. да, ключевое слово await блокирует работающую функцию до тех пор, пока асинхронная функция не "разрешит" значение или не отклонит его с ошибкой, но не заблокирует механизм javascript, который может выполнять другие действия, если он имеет другие дела в ожидании

  2. да, выполнение цикла будет последовательным

Обо всем этом можно прочитать на http://javascript.info/async.

Ответ 6

Объявление 1. Да. Нет.

Объявление 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();

Ответ 7

Ожидает ли блокирование цикла? Или i продолжаю увеличиваться в ожидании?

Нет, ожидание не заблокирует цикл. Да, i продолжаю увеличиваться во время цикла.

Является ли порядок do_something_with_result() гарантированным последовательным по отношению к i? Или это зависит от того, насколько быстро ожидаемая функция подходит для каждого i?

Порядок do_something_with_result() гарантируется последовательно, но не относится к i. Это зависит от того, как быстро работает ожидаемая функция.

Все вызовы some_slow_async_function(), т. 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();