Difference between Javascript async functions and Web workers?

試著忘記壹切 提交于 2019-12-03 11:27:23

问题


Threading-wise, what's the difference between web workers and functions declared as

async function xxx()
{
}

?

I am aware web workers are executed on separate threads, but what about async functions? Are such functions threaded in the same way as a function executed through setInterval is, or are they subject to yet another different kind of threading?


回答1:


Async functions are just syntactic sugar around Promises and they are wrappers for Callbacks. So basically, when you await something the JS-engine continues with other things until the callback you are waiting for calls back.

Whether another thread is involved depends on what you are awaiting in the async function. If it is a timer (setTimeout), an internal timer is set and the JS-thread continues with other stuff until the timer is done and then it continues the execution.

This behaviour is somewhat the same with every function taking a callback or returning a promise. However some of them, especially in the Node.js environment (fetch, fs.readFile) will start another thread internally. You only hand over some arguments and receive the results when the thread is done. With WebWorkers however, you control another thread directly. For shure you can await actions from that other thread too:

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

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

TL;DR:

JS  <---> callbacks / promises <--> internal Thread / Webworker



回答2:


In contrast to WebWorkers, async functions are never guaranteed to be executed on a separate thread.

They just don't block the whole thread until their response arrives. You can think of them as being registered as waiting for a result, let other code execute and when their response comes through they get executed; hence the name asynchronous programming.

This is achieved through a message queue, which is a list of messages to be processed. Each message has an associated function which gets called in order to handle the message.

Doing this:

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

will simply add the callback function (that logs to the console) to the message queue. When it's 1000ms timer elapses, the message is popped from the message queue and executed.

While the timer is ticking, other code is free to execute. This is what gives the illusion of multithreading.

The setTimeout example above uses callbacks. Promises and async work the same way at a lower level — they piggyback on that message-queue concept, but are just syntactically different.




回答3:


Workers are also accessed by asynchronous code (i.e. Promises) however Workers are a solution to the CPU intensive tasks which would block the thread that the JS code is being run on; even if this CPU intensive function is invoked asynchronously.

So if you have a CPU intensive function like renderThread(duration) and if you do like

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

Even if second one takes less time to complete it will only be invoked after the first one releases the CPU thread. So we will get first 1 and then 2 on console.

However had these two function been run on separate Workers, then the outcome we expect would be 2 and 1 as then they could run concurrently and the second one finishes and returns a message earlier.

So for basic IO operations standard single threaded asynchronous code is very efficient and the need for Workers arises from need of using tasks which are CPU intensive and can be segmented (assigned to multiple Workers at once) such as FFT and whatnot.




回答4:


Async functions have nothing to do with web workers or node child processes - unlike those, they are not a solution for parallel processing on multiple threads.

An async function is just1 syntactic sugar for a function returning a promise then() chain.

async function example() {
    await delay(1000);
    console.log("waited.");
}

is just the same as

function example() {
    return Promise.resolve(delay(1000)).then(() => {
        console.log("waited.");
    });
}

These two are virtually indistinguishable in their behaviour. The semantics of await or a specified in terms of promises, and every async function does return a promise for its result.

1: The syntactic sugar gets a bit more elaborate in the presence of control structures such as if/else or loops which are much harder to express as a linear promise chain, but it's still conceptually the same.

Are such functions threaded in the same way as a function executed through setInterval is?

Yes, the asynchronous parts of async functions run as (promise) callbacks on the standard event loop. The delay in the example above would implemented with the normal setTimeout - wrapped in a promise for easy consumption:

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



回答5:


I want to add my own answer to my question, with the understanding I gathered through all the other people's answers:

Ultimately, all but web workers, are glorified callbacks. Code in async functions, functions called through promises, functions called through setInterval and such - all get executed in the main thread with a mechanism akin to context switching. No parallelism exists at all.

True parallel execution with all its advantages and pitfalls, pertains to webworkers and webworkers alone.

(pity - I thought with "async functions" we finally got streamlined and "inline" threading)




回答6:


Here is a way to call standard functions as workers, enabling true parallelism. It's an unholy hack written in blood with help from satan, and probably there are a ton of browser quirks that can break it, but as far as I can tell it works.

[constraints: the function header has to be as simple as function f(a,b,c) and if there's any result, it has to go through a return statement]

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

}

So, assuming you have this potentially cpu intensive function...

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

...you can now call it like this:

Async(HeavyWorkload, [1000, 1000],
function(result)
{
 console.log(result);
}
);


来源:https://stackoverflow.com/questions/49092985/difference-between-javascript-async-functions-and-web-workers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!