Не понял асинхронность JavaScript

181
16 декабря 2019, 12:30

Есть две асинхронные функции:

async function a() {
    for (var i = 0; i <= 10000; i++) {
         console.log(`${i} (a)`);
    }
}
async function b() {
    for (var i = 0; i <= 10000; i++) {
         console.log(`${i} (b)`);
    }
}

и функция c, вызывающая функции a и b:

function c() {
    a();
    b();
}

Если вызвать функцию c, то выводом будет последовательное выполнение функции a (все 10000 итераций), затем функции b.

Если функции a и b изменить таким образом:

async function a() {
    return new Promise((resolve) => {
        for (var i = 0; i <= 10000; i++) {
             console.log(`${i} (a)`);
         }
         resolve();
     });
 }
 async function b() {
     return new Promise((resolve) => {
         for (var i = 0; i <= 10000; i++) {
              console.log(`${i} (b)`);
         }
         resolve();
    });
}

То результат останется прежним. Мне нужно сделать так, чтобы их выводы смешались, чтобы выводилось условно

  • ...
  • 1112 (a)
  • 1112 (b)
  • 1113 (a)
  • 1114 (a)
  • 1113 (b)
  • ...

А не

  • ...
  • 9999 (a)
  • 10000 (a)
  • 0 (b)
  • 1 (b)
  • ...

Как сделать это правильно и можно ли вообще?

Answer 1

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

async function a() { 
  for (var i = 0; i <= 10; i++) { 
    console.log(`${i} (a)`); 
    // Возвращаем выполнение браузеру, чтобы он повыполнял другие задачи 
    // (например, цикл в b) 
    await null; 
  } 
} 
 
async function b() { 
  for (var i = 0; i <= 10; i++) { 
    console.log(`${i} (b)`); 
    // Возвращаем выполнение браузеру, чтобы он повыполнял другие задачи 
    // (например, цикл в a) 
    await null; 
  } 
} 
 
function c() { 
  a().then(() => console.log('a отработал')); 
  b().then(() => console.log('b отработал')); 
} 
 
c();

Вариант, когда цикл передаёт управление другим задачам не каждый раз, а через несколько итераций:

async function a() { 
  for (var i = 0; i <= 10; i++) { 
    console.log(`${i} (a)`); 
    if (i % 3 === 2) await null; 
  } 
} 
 
async function b() { 
  for (var i = 0; i <= 10; i++) { 
     console.log(`${i} (b)`); 
     if (i % 3 === 2) await null; 
  } 
} 
 
function c() { 
  a().then(() => console.log('a отработал')); 
  b().then(() => console.log('b отработал')); 
} 
 
c();

Или пример с асинхронной обёрткой над setTimeout:

function sleep(ms) { 
  return new Promise((resolve) => setTimeout(resolve, ms)); 
} 
 
async function a() { 
  for (var i = 0; i <= 10; i++) { 
    console.log(`${i} (a)`); 
    // Спим полсекунды, чтобы поработал кто-то ещё 
    await sleep(500); 
  } 
} 
 
async function b() { 
  for (var i = 0; i <= 10; i++) { 
     console.log(`${i} (b)`); 
     // Спим четверть секунды для разнообразия 
     await sleep(250); 
  } 
} 
 
function c() { 
  a().then(() => console.log('a отработал')); 
  b().then(() => console.log('b отработал')); 
} 
 
c();

Answer 2

Чтобы вывод мог перемешиваться, каждая из итерация тоже должна быть асинхронной.

Например используя setTimeout и await

async function a() { 
  for (var i = 0; i <= 10; i++) { 
    await new Promise(r => setTimeout(() => r(console.log(`${i} (a)`)))); 
  } 
} 
 
async function b() { 
  for (var i = 0; i <= 10; i++) { 
    await new Promise(r => setTimeout(() => r(console.log(`${i} (b)`)))); 
  } 
} 
 
function c() { 
  a(); 
  b(); 
} 
 
c();

READ ALSO
Проблемы кроссбразерности

Проблемы кроссбразерности

В Safari не работает, но работает в Google Chrome, OperaНе могу понять в чем проблема

149
разделить один аргумент ф-ци на два

разделить один аргумент ф-ци на два

знакомый задал задачу, как сделать следующее:

163