bugfix> javascript > 投稿

async、await、setTimeout()について質問があります。 遅いプロセスには非同期関数を使用すると思いました。そこで、大きなループで試しました。私のコンピューターでは、次のコードを実行するのに数秒かかります。

function slowFunction() {
    return new Promise(resolve => {
        setTimeout(() => {
            for (let i = 0; i < 4000000000; i++) {};
            resolve('Ready at ' + new Date().toLocaleTimeString('de'));
        }, 0);
    });
};

console.log('Start: ' + new Date().toLocaleTimeString('de'));
(async () => {
    console.log('Call slow function.');
    console.log(await slowFunction());
})();
console.log('There is no need to wait for the slow function: ' + new Date().toLocaleTimeString('de'));

出力は次のとおりです。

Start: 16:39:20
Call slow function.
There is no need to wait for the slow function: 16:39:20
Ready at 16:39:23

そして今、質問:次のコードとの違いは何ですか:

function slowFunction() {
    return new Promise(resolve => {
        for (let i = 0; i < 4000000000; i++) {};
        resolve('Ready at ' + new Date().toLocaleTimeString('de'));
    });
};
console.log('Start: ' + new Date().toLocaleTimeString('de'));
(async () => {
    console.log('Call slow function.');
    console.log(await slowFunction());
})();
console.log('There is no need to wait for the slow function: ' + new Date().toLocaleTimeString('de'));

出力は次のとおりです。

Start: 16:39:20
Call slow function.
There is no need to wait for the slow function: 16:39:23
Ready at 16:39:23

最初の例では、非同期のように見えます。 2番目の例では、関数はループの終わりを待ちます。

setTimeoutを使用する必要がありますか、コードにエラーがありますか、それとも間違っていますか?どちらの場合も、解決ステートメントは大きなループの背後にあります。

asyncとawaitのほとんどの例ではsetTimeoutを使用しましたが、それは単にブレークをシミュレートするためだと思います。

事前にご協力いただきありがとうございます。

最高の挨拶 パスカル

回答 2 件
  • TL:DR

    約束と async  関数はコードを別のスレッドにオフロードしません。その長期実行プロセスをメインスレッドから移動したい場合は、ブラウザーでWebワーカーを調べ、Node.jsで子プロセスを調べます。

    詳細

    約束と async  関数(promiseを作成および使用するための単なる構文)は、処理を他のスレッドに移動せず、プロセスを開始した同じスレッドで実行されます。ののみ 彼らがやることは、 then  および catch  コールバックは非同期に呼び出されます。それらはあなたのコードを非同期にしません(それ以外のこと、コールバックが非同期的に起こることを保証します)。

    したがって、 setTimeout を使用する最初のブロック  タイムアウトを設定してプロミスを返すだけで、タイムアウトが期限切れになると、スロー実行プロセスの実行中にメインスレッドがブロックされます。これは、ブロッキングが少し発生したときに変更されるだけで、ブロッキングの事実は変更されません。

    ここでその効果を確認できます。長時間実行されるプロセスが発生したときにカウンターがどのように一時停止するかに注目してください。

    function slowFunction() {
      return new Promise(resolve => {
        setTimeout(() => {
          const stop = Date.now() + 2000;
          while (Date.now() < stop) {
            // busy wait (obviously, never really do this)
          }
        }, 1000);
      });
    };
    console.log("before slowFunction");
    slowFunction()
      .then(() => {
        console.log("then handler on slowFunction's promise");
      })
      .catch(console.error);
    console.log("after slowFunction");
    let counter = 0;
    const timer = setInterval(() => {
      console.log(++counter);
    }, 100);
    setTimeout(() => {
      clearInterval(timer);
      console.log("done");
    }, 3000);
    
    
    .as-console-wrapper {
      max-height: 100% !important;
    }
    
    

    setTimeout を使用していない2番目のブロック  promise executor関数( new Promise に渡す関数 )すぐに同期的に実行されますが、非同期にするために何もしていません。

    ここで確認できます。カウンターはすぐに一時停止します。

    function slowFunction() {
      return new Promise(resolve => {
        const stop = Date.now() + 2000;
        while (Date.now() < stop) {
          // busy wait (obviously, never really do this)
        }
      });
    };
    console.log("before slowFunction");
    slowFunction()
      .then(() => {
        console.log("then handler on slowFunction's promise");
      })
      .catch(console.error);
    console.log("after slowFunction");
    let counter = 0;
    const timer = setInterval(() => {
      console.log(++counter);
    }, 100);
    setTimeout(() => {
      clearInterval(timer);
      console.log("done");
    }, 3000);
    
    
    .as-console-wrapper {
      max-height: 100% !important;
    }
    
    

    私たちも見ていませんslowFunctionの前 長時間実行されるコードが終了するまでログが表示されます。ブラウザが再描画する機会を得ることはなかったため、スレッドが占有されました。

    async について  関数: async のコード  関数始まる 同期、および最初の await まで同期  (または setTimeout などの他の構成体 、後で実行するようにスケジュールします)。その後のコードのみが非同期です(待機する必要があったため)。

    以下に例を示します。

    async function foo() {
      console.log("before await");
      await Promise.resolve();
      console.log("after await");
    }
    console.log("before foo");
    foo()
      .then(() => {
        console.log("then handler on foo's promise");
      })
      .catch(console.error);
    console.log("after foo");
    
    

    その出力は次のとおりです。

    fooの前
    待つ前に
    fooの後
    待ってから
    fooの約束のハンドラー
    

    気づく方法待つ前に 前に発生するfooの後; foo の呼び出しと同期しています 。しかしその後待ってから 後まで発生しません( await Promise.resolve() のため  それに続くコードを非同期に発生させる必要があります。 then の構文糖衣 、約束が既に解決されている場合でも、ハンドラを同期的に呼び出さないことを約束します)。

  • 違いは、これは完全に同期的なコードであるということです。

    return new Promise(resolve => {
        for (let i = 0; i < 4000000000; i++) {};
        resolve('Ready at ' + new Date().toLocaleTimeString('de'));
    });
    
    

    このステートメントはJavaScriptスレッドをブロックし、40億回の反復がすべて完了するまで強制的に待機させます。それから 次のステートメントに進みます。 console.log 以来  この後に実行すると、そのループが終了するまで実行されません。

    だからこそ、違いが見られます。

あなたの答え