1. ホーム
  2. javascript

[解決済み] JavaScript の Promise が解決するのを待ってから機能を再開するにはどうすればよいですか?

2022-03-01 22:13:03

質問

ユニットテストを行っています。テストフレームワークは、ページをiFrameにロードし、そのページに対してアサーションを実行します。 各テストが始まる前に、私は Promise を設定し、iFrameの onload イベントを呼び出して resolve() を設定すると、iFrameの src そしてプロミスを返します。

ということで、そのまま loadUrl(url).then(myFunc) を実行する前に、ページが読み込まれるのを待ちます。 myFunc があります。

私はこの種のパターンをテストのいたるところで使っています (URL を読み込むためだけではなく)、主に DOM への変更を可能にするためです (たとえば、ボタンをクリックするのを真似たり、div が隠れたり現れたりするのを待ったりします)。

この設計の欠点は、数行のコードを含む匿名関数を常に書いていることです。 さらに、回避策もあるのですが(QUnitの assert.async() )、プロミスを定義するテスト関数は、プロミスが実行される前に完了します。

の値を取得する方法はないのでしょうか? Promise のように、解決するまで待つ(ブロック/スリープ)ことができます。 IAsyncResult.WaitHandle.WaitOne() . JavaScriptがシングルスレッドであることは知っていますが、だからといって関数が降伏できないとは限らないと思っています。

要するに、以下のように正しい順序で結果を吐き出させる方法はないのでしょうか?

function kickOff() {
  return new Promise(function(resolve, reject) {
    $("#output").append("start");
    
    setTimeout(function() {
      resolve();
    }, 1000);
  }).then(function() {
    $("#output").append(" middle");
    return " end";
  });
};

function getResultFrom(promise) {
  // todo
  return " end";
}

var promise = kickOff();
var result = getResultFrom(promise);
$("#output").append(result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>

解決方法は?

<ブロッククオート

プロミスから値を取得する方法や、プロミスから値を取得する方法はないでしょうか? が解決するまで待つ(ブロック/スリープ)。 IAsyncResult.WaitHandle.WaitOne()。JavaScriptが シングルスレッドですが、だからといって、関数が は降伏できない。

現世代のブラウザに搭載されているJavascriptには wait() または sleep() 他のものを実行することができます。 ですから、単純にあなたが求めていることはできないのです。 その代わり、非同期オペレーションがあり、それが完了したらあなたを呼び出します(あなたがプロミスを使用していたように)。

その理由のひとつは、Javascriptがシングルスレッドであることです。 シングルスレッドが回転している場合、その回転しているスレッドが終了するまで、他のJavascriptを実行することはできません。 ES6では yield とジェネレータを使うことで、このような協力なトリックが可能になりますが、インストールされているブラウザの広い範囲でこれらを使えるようになるには、まだかなり時間がかかります(使用するJSエンジンを制御するサーバーサイド開発では使用可能です)。


プロミス・ベースのコードを注意深く管理することで、多くの非同期処理の実行順序を制御することができます。

あなたのコードで実現しようとしている順序を正確に理解しているわけではありませんが、あなたの既存の kickOff() 関数を使用し、その後に .then() ハンドラを呼び出した後、そのハンドラに

function kickOff() {
  return new Promise(function(resolve, reject) {
    $("#output").append("start");
    
    setTimeout(function() {
      resolve();
    }, 1000);
  }).then(function() {
    $("#output").append(" middle");
    return " end";
  });
}

kickOff().then(function(result) {
    // use the result here
    $("#output").append(result);
});

これは、保証された順序で出力を返します - このように。

start
middle
end

2018年(この回答が書かれてから3年後)のアップデート。

コードをトランスパイルするか、以下のような ES7 の機能をサポートする環境でコードを実行する場合。 asyncawait を使用することができるようになりました。 await を使えば、プロミスの結果を待っているように見せることができます。 これはまだプロミスを使ったプログラミングです。 これはまだJavascriptのすべてをブロックするものではありませんが、より親しみやすい構文でシーケンシャルな操作を書くことができるようになります。

ES6のやり方の代わりに。

someFunc().then(someFunc2).then(result => {
    // process result here
}).catch(err => {
    // process error here
});

こんなことができるんですね。

// returns a promise
async function wrapperFunc() {
    try {
        let r1 = await someFunc();
        let r2 = await someFunc2(r1);
        // now process r2
        return someValue;     // this will be the resolved value of the returned promise
    } catch(e) {
        console.log(e);
        throw e;      // let caller know the promise was rejected with this reason
    }
}

wrapperFunc().then(result => {
    // got final result
}).catch(err => {
    // got error
});

async 関数は、最初の await は関数本体の中でヒットするので、呼び出し側にとって非同期関数はまだノンブロッキングであり、呼び出し側はまだ返されたプロミスを処理してそのプロミスから結果を取得しなければなりません。 しかし async を使用すると、よりシーケンシャルなコードを書くことができます。 await 約束について 心に留めておいてください await はプロミスを待っている間だけ役に立つことをします。 async/await 非同期処理はすべてプロミスベースでなければなりません。