Что касается потоков, то в чем разница между веб-работниками и функциями, объявленными как

async function xxx()
{
}

?

Я знаю, что веб-работники выполняются в отдельных потоках, но как насчет асинхронных функций? Пропускаются ли такие функции так же, как функция, выполняемая через setInterval, или они подвергаются еще одному другому виду потоков?

27
resle 4 Мар 2018 в 10:20

5 ответов

Лучший ответ

Функции Async являются просто синтаксическим сахаром вокруг Promises и являются обертками для Callbacks. Таким образом, в основном, когда вы await что-то JS-движок продолжает с другими вещами, пока callback вы ожидаете ответных вызовов.

Будет ли задействован другой поток, зависит от того, что вы ожидаете в функции async. Если это таймер (setTimeout), устанавливается внутренний таймер, и JS-поток продолжает выполнять другие действия, пока не завершится таймер, а затем продолжит выполнение.

Это поведение примерно одинаково с каждой функцией, принимающей обратный вызов или возвращающей promise. Однако некоторые из них, особенно в среде Node.js (fetch, fs.readFile), запустят другой поток внутри . Вы только передаете некоторые аргументы и получаете результаты, когда поток завершен. Однако с WebWorkers вы напрямую управляете другим потоком. Для уверенности вы можете await выполнять действия из этого другого потока:

const workerDone = new Promise(res => window.onmessage = res);

(async function(){
    const result = await workerDone;
        //...
})();

TL ; DR :

JS  <---> callbacks / promises <--> internal Thread / Webworker
10
Mike B. 21 Май 2019 в 15:00

Доступ к рабочим также осуществляется с помощью асинхронного кода (т. Е. Обещаний), однако рабочие являются решением задач с интенсивным использованием ЦП, которые блокируют поток, в котором выполняется код JS; даже если эта интенсивная загрузка процессора вызывается асинхронно.

Так что, если у вас есть интенсивная загрузка процессора, такая как renderThread(duration), и если вам нравится

new Promise((v,x) => setTimeout(_ => (renderThread(500), v(1)),0)
    .then(v => console.log(v);
new Promise((v,x) => setTimeout(_ => (renderThread(100), v(2)),0)
    .then(v => console.log(v);

Даже если второй процесс занимает меньше времени, он будет вызван только после того, как первый освободит поток ЦП. Итак, сначала мы получим 1, а затем 2 на консоли.

Однако, если бы эти две функции были запущены на отдельных рабочих, то мы ожидаем, что результатом будет 2 и 1, так как тогда они могут работать одновременно, а вторая завершится и вернет сообщение раньше.

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

5
Redu 4 Мар 2018 в 09:23

Я хочу добавить свой собственный ответ на мой вопрос, с пониманием, которое я собрал через ответы всех других людей:

В конечном счете, все, кроме веб-работников, прославленные обратные вызовы. Код в асинхронных функциях, функции, вызываемые через обещания, функции, вызываемые через setInterval и т. Д. - все они выполняются в основном потоке с помощью механизма, похожего на переключение контекста. Никакого параллелизма не существует вообще.

Истинное параллельное выполнение со всеми его преимуществами и недостатками относится только к веб-работникам и веб-работникам.

(Жаль - я подумал, что с помощью «асинхронных функций» мы, наконец, получили упрощенную и «встроенную» многопоточность)

2
resle 5 Мар 2018 в 06:39

В отличие от WebWorkers, функции async никогда не гарантированно выполняются в отдельном потоке.

Они просто не блокируют весь поток, пока не придет их ответ. Вы можете думать о них как о том, что они зарегистрированы как ожидающие результата, позволяющие другому коду выполняться, и когда их ответ приходит, они выполняются; отсюда и название асинхронного программирования.

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

Делая это:

setTimeout(() => {
    console.log('foo')
}, 1000)

Просто добавит функцию обратного вызова (которая ведет на консоль) в очередь сообщений. По истечении таймера 1000 мс сообщение извлекается из очереди сообщений и выполняется.

Пока таймер тикает, другой код может свободно выполняться. Это то, что дает иллюзию многопоточности.

В приведенном выше примере setTimeout используются обратные вызовы. Promises и async работают одинаково на более низком уровне - они используют эту концепцию очереди сообщений, но синтаксически отличаются друг от друга.

7
Mike B. 21 Май 2019 в 15:07

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

[ ограничения : заголовок функции должен быть таким же простым, как функция f (a, b, c) , и если есть какой-либо результат, он должен пройти через возвращаемая инструкция]

function Async(func, params, callback)
{ 
 // ACQUIRE ORIGINAL FUNCTION'S CODE
 var text = func.toString(); 


 // EXTRACT ARGUMENTS
 var args = text.slice(text.indexOf("(") + 1, text.indexOf(")")); 
 args     = args.split(",");
 for(arg of args) arg = arg.trim();


 // ALTER FUNCTION'S CODE:
 // 1) DECLARE ARGUMENTS AS VARIABLES
 // 2) REPLACE RETURN STATEMENTS WITH THREAD POSTMESSAGE AND TERMINATION
 var body = text.slice(text.indexOf("{") + 1, text.lastIndexOf("}")); 
 for(var i = 0, c = params.length; i<c; i++) body = "var " + args[i] + " = " + JSON.stringify(params[i]) + ";" + body;
 body = body + " self.close();"; 
 body = body.replace(/return\s+([^;]*);/g, 'self.postMessage($1); self.close();');


 // CREATE THE WORKER FROM FUNCTION'S ALTERED CODE
 var code   = URL.createObjectURL(new Blob([body], {type:"text/javascript"}));
 var thread = new Worker(code);


 // WHEN THE WORKER SENDS BACK A RESULT, CALLBACK AND TERMINATE THE THREAD
 thread.onmessage =
 function(result)
 {
  if(callback) callback(result.data);

  thread.terminate();
 }

}

Итак, если у вас есть эта потенциально интенсивная функция процессора ...

function HeavyWorkload(nx, ny) 
{
 var data = [];

 for(var x = 0; x < nx; x++)
 {
  data[x] = [];

  for(var y = 0; y < ny; y++)
  {
   data[x][y] = Math.random();
  }
 }

 return data;
}

... теперь вы можете назвать это так:

Async(HeavyWorkload, [1000, 1000],
function(result)
{
 console.log(result);
}
);
3
resle 11 Мар 2018 в 05:48