web-dev-qa-db-ja.com

ブラウザで非同期が本当にブロックされないのを待っていますか?

TypeScriptとネイティブPromiseを使用してSPAの機能をいじくり回してきましたが、長期実行関数をリファクタリングして約束を返す非同期関数にリファクタリングしても、UIはまだ応答しません。

だから私の質問は:

  • 新しいasync/await機能は、ブラウザでUIのブロックを回避するのにどの程度役立ちますか?実際にレスポンシブUIを取得するためにasync/awaitを使用するときに実行する必要がある特別な追加手順はありますか?

  • 誰かがフィドルを作成して、async/awaitがUIをレスポンシブにする方法を示すことができますか?

  • Async/awaitは、setTimeoutやXmlHttpRequestなどの以前の非同期機能とどのように関連していますか?

30
prmph

await pは、promise pが解決するときに、関数の残りの実行をスケジュールします。それで全部です。

asyncを使用すると、awaitを使用できます。それが(ほぼ)すべてです(約束に結果をラップします)。

一緒にすると、ノンブロッキングコードがより単純なブロッキングコードのように読み取られます。コードのブロックを解除しません。

レスポンシブUIの場合、CPUに負荷のかかる作業を 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));
54
jib

asyncは、非同期コードを構築するよりエレガントな方法です。新しい機能は許可されません。コールバックやプロミスよりも優れた構文です。

したがって、asyncを使用して「非同期にする」ことはできません。多くのCPUベースの処理を実行する必要があるコードがある場合、asyncはUIを魔法のように反応させません。あなたがする必要があるのは web workers のようなものを使用することです-are UIをレスポンシブにするためにCPUにバインドされた作業をバックグラウンドスレッドにプッシュする適切なツール。

14
Stephen Cleary

JavaScriptはシングルスレッドであり、UIと同じスレッドで実行されます。したがって、すべてのJavaScriptコードはUIをブロックします。他の人が述べたように、Webワーカーは他のスレッドでコードを実行するために使用できますが、制限があります。

非同期関数と通常の関数の違いは、約束を返すことです。コールバックを使用すると、コードの実行を延期できます。コードの実行は、関数呼び出しの結果を処理し、UIが何らかの作業を行えるようにします。次の3つの例には同じ効果があります。

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

3つのケースすべてで、コンソールはhi、lo、1をログに記録します。1が出力される前に、UIはユーザー入力を処理したり、更新を描画したりできます。最初の2つのケースで最後に出力される理由1は、promiseのコールバックがすぐに実行されないためです。

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も出力します。promiseのコールバックと同様に、awaitの後のコードはすぐには実行されません。

10
a better oliver