Является ли асинхронное ожидание по-настоящему неблокирующим в браузере?

Я играю с функцией в SPA, используя TypeScript и native Promises, и я замечаю, что даже если я реорганизую долговременную функцию в функцию async, возвращающую обещание, пользовательский интерфейс по-прежнему не отвечает.

Итак, мои вопросы:

  • Как именно новая функция async/await помогает избежать блокировки пользовательского интерфейса в браузере? Существуют ли какие-либо специальные дополнительные шаги, которые нужно предпринять при использовании async/wait, чтобы фактически получить отзывчивый интерфейс?

  • Может ли кто-нибудь создать скрипку, чтобы продемонстрировать, как async/await помогает сделать пользовательский интерфейс отзывчивым?

  • Как async/await относятся к предыдущим асинхронным функциям, таким как setTimeout и XmlHttpRequest?

Ответ 1

await p назначает выполнение остальной части вашей функции, когда обещает p. Это все.

async позволяет использовать await. Это (почти) все, что он делает (это также завершает ваш результат в обещании).

Вместе они делают неблокирующий код прочитанным как более простой код блокировки. Они не разблокируют код.

Для чувствительного пользовательского интерфейса выгружайте интенсивную работу процессора в поток worker и передавайте ему сообщения:

async function brutePrime(n) {
  function work({data}) {
    while (true) {
      let d = 2;
      for (; d < data; d++) {
        if (data % d == 0) break;
      }
      if (d == data) return self.postMessage(data);
      data++;
    }
  }

  let b = new Blob(["onmessage =" + work.toString()], {type: "text/javascript"});
  let worker = new Worker(URL.createObjectURL(b));
  worker.postMessage(n); 
  return await new Promise(resolve => worker.onmessage = e => resolve(e.data));
}

(async () => {
  let n = 700000000;
  for (let i = 0; i < 10; i++) {
    console.log(n = await brutePrime(n + 1));
  }
})().catch(e => console.log(e));

Ответ 2

async - более элегантный способ структурирования асинхронного кода. Он не позволяет создавать новые возможности; это просто лучший синтаксис, чем обратные вызовы или promises.

Итак, async нельзя использовать для "создания чего-то асинхронного". Если у вас есть код, который должен выполнять большую часть обработки на основе ЦП, async не будет волшебно заставлять пользовательский интерфейс реагировать. Что вам нужно сделать, это использовать что-то вроде веб-работников, которые являются надлежащим инструментом для перевода работы, связанной с ЦП, на фоновый поток в порядке чтобы сделать пользовательский интерфейс отзывчивым.

Ответ 3

JavaScript является однопоточным и работает в том же потоке, что и пользовательский интерфейс. Таким образом, весь код JavaScript блокирует пользовательский интерфейс. Как упоминалось другими веб-рабочими, можно использовать для запуска кода в других потоках, но у них есть ограничения.

Разница между асинхронными функциями и регулярными заключается в том, что они возвращают обещание. Используя обратный вызов, вы можете отложить выполнение кода, который обрабатывает результат вызова функции и тем самым позволяет пользовательскому интерфейсу выполнять некоторую работу. Следующие три примера имеют тот же эффект:

async function foo() {
  console.log("hi");
  return 1; 
}
foo().then(result => console.log(result))
console.log("lo");

function foo() {
  console.log("hi");
  return 1; 
}
Promise.resolve(foo()).then(result => console.log(result))
console.log("lo");

function foo() {
  console.log("hi");
  return 1; 
}
const result = foo();
setTimeout(() => console.log(result));
console.log("lo");

Во всех трех случаях консольные журналы hi, lo, 1. Перед печатью 1 пользовательский интерфейс может обрабатывать ввод пользователя или нарисовать обновления. Причина 1, которая напечатана последним в первых двух случаях, заключается в том, что обратные вызовы для promises не выполняются немедленно.

await позволяет сделать это без обратных вызовов:

async function foo() {
  console.log("hi");
  return 1; 
}

async function bar() {
  const result = await foo();
  console.log(result);
}

bar();
console.log("lo"); 

Это также напечатает hi, lo, 1. Как и обратный вызов для обещания, код после await никогда не выполняется сразу.