web-dev-qa-db-ja.com

JavaScript非同期関数とWebワーカーの違いは何ですか?

スレッド化すると、Webワーカーと次のように宣言された関数の違いは何ですか

async function xxx()
{
}

Webワーカーが別のスレッドで実行されることは承知していますが、非同期関数はどうなりますか?そのような関数は、setIntervalを介して実行される関数と同じ方法でスレッド化されますか、それともさらに別の種類のスレッド化の影響を受けますか?

18
resle

Async関数はPromisesの周りの構文糖衣であり、Callbacksのラッパーです。つまり、基本的には、何かがawaitになると、JSエンジンはcallbackがコールバックを待機するまで他の処理を続行します。

別のスレッドが関係しているかどうかは、async関数で待機している内容によって異なります。タイマー(setTimeout)の場合、内部タイマーが設定され、JSスレッドはタイマーが終了するまで他の処理を続行し、その後、実行を継続します。

この動作は、すべての関数がコールバックを受け取るか、promiseを返す場合と多少同じです。ただし、それらの一部、特にNode.js環境では(fetchfs.readFile)別のスレッドを開始します内部的に。一部の引数のみを渡して、スレッドが完了したときに結果を受け取ります。ただし、WebWorkersでは、別のスレッドを直接制御します。 Shureの場合は、他のスレッドからのawaitアクションも実行できます。

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

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

TL; DR:

JS  <---> callbacks / promises <--> internal Thread / Webworker
10
Jonas Wilms

WebWorkersとは対照的に、async関数は別のスレッドで実行されることが保証されていません。

応答が到着するまでスレッド全体をブロックしません。あなたはそれらが結果としてwaitingとして登録されていると考えることができ、他のコードを実行させ、それらの応答が到着すると実行されます。そのため、名前は非同期プログラミングです。

これは、処理されるメッセージのリストであるメッセージキューを通じて実現されます。各メッセージには、メッセージを処理するために呼び出される関数が関連付けられています。

これを行う:

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

(コンソールにログを記録する)コールバック関数をメッセージキューに追加するだけです。 1000msタイマーが経過すると、メッセージはメッセージキューからポップされて実行されます。

タイマーが作動している間、他のコードは自由に実行できます。これがマルチスレッドのような錯覚を与えます。

上記のsetTimeoutの例では、コールバックを使用しています。 Promisesasyncは、下位レベルでも同じように機能します。これらは、メッセージキューの概念に便乗していますが、構文的には異なります。

7
Nik Kyriakides

ワーカーは非同期コード(Promiseなど)からもアクセスされますが、JSコードが実行されているスレッドをブロックするCPU集中型のタスクに対するソリューションです。このCPU集中型の関数が非同期に呼び出された場合でも。

したがって、renderThread(duration)のようなCPU集中型の関数があり、

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

2番目のものは完了までの時間が短い場合でも、最初のものがCPUスレッドを解放した後でのみ呼び出されます。したがって、最初に1、次にコンソールで2を取得します。

ただし、これらの2つの関数が別々のワーカーで実行された場合、期待される結果は21になります。これらは同時に実行でき、2番目の関数が終了してより早くメッセージを返すためです。

基本的なIO操作の場合、標準のシングルスレッド非同期コードは非常に効率的であり、CPUを集中的に使用し、セグメント化(一度に複数のワーカーに割り当てる)できるタスクを使用する必要があるため、ワーカーの必要性が生じます。 FFTとは別のものです。

5
Redu

非同期関数は、Webワーカーやノードの子プロセスとは何の関係もありません-それらとは異なり、複数のスレッドでの並列処理のソリューションではありません。

_async function_は1 promise then()チェーンを返す関数の構文糖。

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

と同じです

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

これら2つは、その振る舞いにおいて事実上区別がつきません。 awaitまたはpromiseで指定されたセマンティクス、およびすべての_async function_は、その結果のpromiseを返します。

1:構文砂糖はビット線形約束として表現するのがはるかに難しいif/elseまたはループなどの制御構造の存在下でより複雑になりますチェーンですが、概念的には同じです。

そのような関数は、setIntervalを介して実行される関数と同じ方法でスレッド化されていますか?

はい、_async function_ sの非同期部分は、標準のイベントループで(約束)コールバックとして実行されます。上記の例のdelayは、通常のsetTimeoutを使用して実装されます-簡単に使用できるという約束にラップされています。

_function delay(t) {
    return new Promise(resolve => {
        setTimeout(resolve, t);
    });
}
_
5
Bergi

これは、標準関数をワーカーとして呼び出し、真の並列処理を可能にする方法です。それは悪魔の助けを借りて血で書かれた不誠実なハックであり、おそらくそれを壊すことができるブラウザーの癖がたくさんあるが、私が知る限り、それは機能する。

[constraints:関数のヘッダーは、function f(a、b、c)のように単純である必要があります。結果、returnステートメントを通過する必要があります]

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

}

したがって、この潜在的にCPUを多用する機能があると仮定すると...

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

自分の質問に自分の答えを追加したいのですが、他のすべての人の答えを通して私が理解したことを理解します。

結局のところ、Webワーカーを除くすべてが名誉あるコールバックです。非同期関数、promiseを介して呼び出される関数、setIntervalを介して呼び出される関数などのコード-すべては、コンテキスト切り替えと同様のメカニズムを使用してメインスレッドで実行されます。並列処理はまったく存在しません。

すべての利点と落とし穴を伴う真の並列実行は、WebワーカーとWebワーカーだけに関係します。

(残念-私は「非同期関数」を使って合理化された「インライン」スレッド化を最終的に考えました)

2
resle