Является ли async await действительно неблокирующим в браузере?

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

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

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

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

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


person prmph    schedule 13.03.2017    source источник
comment
Код, который блокирует, все равно будет блокироваться. Если бы этого не было, у вас могла бы быть гонка данных. Идея асинхронных функций заключается в том, что вы можете остановиться, чтобы дождаться последующего выполнения асинхронного кода. Таким образом, вы останавливаетесь, ожидая завершения чего-то асинхронного, например setTimeout, ответа XHR или события щелчка: jsfiddle. net / wgqyayhr (для демонстрации нужен браузер с поддержкой)   -  person    schedule 14.03.2017
comment
async/await не является частью ES7 (ES2016). Он будет частью выпуска этого года, ES2017.   -  person Felix Kling    schedule 16.03.2017


Ответы (5)


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

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

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

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

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));

person jib    schedule 15.03.2017
comment
Очки за рабочий без внешнего файла. Это крутой трюк. - person solarc; 20.07.2017
comment
асинхронные функции также возвращают обещание - person c0de; 25.06.2020

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

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

person Stephen Cleary    schedule 14.03.2017
comment
Я думаю, вы могли бы использовать это так, чтобы остановить блокировку цикла. for (let i = 0; i ‹100000; i ++) {ожидание задержки (10)} задержка асинхронной функции (мс) {возврат нового обещания ((разрешение, отклонение)) =› setTimeout (разрешение, мс)); } - person Will Voelcker; 18.12.2017
comment
@ Martian2049: Да; async построен на неблокирующих обещаниях. - person Stephen Cleary; 24.05.2018
comment
Он не допускает никаких новых возможностей На самом деле позволяет. Итак, async нельзя использовать для создания чего-то асинхронного Но await может. - person a better oliver; 01.11.2018
comment
@abetteroliver: Возможно, мы смотрим на другую семантику. Когда я говорю, что [async] не допускает никаких новых возможностей, я имею в виду, что все, что вы можете делать с async, можно сделать без async использования обратных вызовов / обещаний - что тривиально доказуемо, потому что до недавнего времени async было преобразованием кода во время компиляции без поддержка во время выполнения. - person Stephen Cleary; 02.11.2018
comment
@abetteroliver: Что касается, но await может [делать вещи асинхронными], я думаю о await как о потреблении асинхронности; его нельзя использовать, чтобы заставить синхронный API работать асинхронно (сделать вещи асинхронными). - person Stephen Cleary; 02.11.2018
comment
async доступен уже довольно давно, и преобразование, о котором вы говорите, использует генераторы. И функции генератора, и асинхронные функции сохраняют стек и по существу позволяют приостанавливать выполнение функций. Вы не можете добиться этого с помощью обещаний. Вы правы, конечно, что await не делает API асинхронным, но из-за асинхронной природы (нативных) обещаний другой код может (!) Получить шанс быть выполнен до продолжения функции. - person a better oliver; 10.12.2018

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, которая печатается последней в первых двух случаях, заключается в том, что обратные вызовы для обещаний не выполняются немедленно.

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 никогда не выполняется немедленно.

person a better oliver    schedule 16.03.2017
comment
Что делать, если вы ожидаете bar ()? - person Rodrigo; 25.10.2018
comment
@Rodrigo Вы можете ожидать только внутри асинхронных функций. - person a better oliver; 01.11.2018
comment
Почему hi печатается раньше lo? - person zzzzzzz; 29.03.2019
comment
@zzzzzzz bar вызывается перед console.log("lo") и вызывает foo, который вызывает console.log("hi"). - person a better oliver; 30.03.2019
comment
@abetteroliver мой плохой. Я хотел спросить, почему 1 печатается после lo? await автоматически вводит немного времени ожидания? IOW, почему это поведение работает, несмотря на отсутствие тайм-аутов на сон - person zzzzzzz; 03.04.2019
comment
@zzzzzzz Асинхронные функции возвращают обещание. await ожидает разрешения или отклонения обещания, и это всегда происходит асинхронно. Строго говоря, времени ожидания нет, просто любой синхронный код выполняется до этого. - person a better oliver; 08.04.2019
comment
Я тоже был сбит с толку, затем я копирую и вставляю код в babeljs.io/repl, и вы видите это фактически превратилось в 2 функции генератора. Я думаю вот почему - person Alan Dong; 03.03.2020

Это ясно из описания на developer.mozilla.org что он неблокирующий:

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

person Per Quested Aronsson    schedule 12.10.2020
comment
Ключевое слово await заставляет среду выполнения JavaScript приостанавливать выполнение вашего кода в этой строке, не позволяя выполнять дальнейший код тем временем, пока вызов асинхронной функции не вернет свой результат - очень полезно, если последующий код полагается на этот результат! - person Rachanee Saengkrajai; 14.01.2021

Я опаздываю на вечеринку здесь. Я хотел проверить как синхронные, так и асинхронные варианты использования.

Чтобы было ясно, async / await не создает синхронный код. Он только добавляет syntactic sugar, что делает код синхронным. Под оболочкой логика Promise продолжает реализовывать non-preemptive multitasking.

В примере gist вы можете запустить пример с параметром командной строки, который выбирает блокирующий или неблокирующий ввод CLI. asyncExample.js

person Greg Smith    schedule 09.07.2021