web-dev-qa-db-ja.com

JavaScript Promiseが解決するのを待ってから機能を再開する方法は?

私はいくつかの単体テストを行っています。テストフレームワークはページをiFrameにロードし、そのページに対してアサーションを実行します。各テストを開始する前に、i_FrameのPromiseイベントを設定してresolve()を呼び出し、iFrameのonloadを設定し、promiseを返すsrcを作成します。

そのため、loadUrl(url).then(myFunc)を呼び出すだけで、myFuncを実行する前にページがロードされるのを待ちます。

主にDOMの変更を許可するために(URLの読み込みだけでなく)、テストのあらゆる場所でこの種のパターンを使用します(たとえば、ボタンをクリックしてmimickし、divが非表示および表示になるのを待ちます)。

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

.NETのIAsyncResult.WaitHandle.WaitOne()のように、Promiseから値を取得する方法、または解決するまで待つ(ブロック/スリープする)方法があるかどうか疑問に思っています。 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>
57
dx_over_dt

.NETのIAsyncResult.WaitHandle.WaitOne()のように、Promiseから値を取得する方法、または解決するまで待機(ブロック/スリープ)する方法があるかどうか疑問に思っています。 JavaScriptがシングルスレッドであることは知っていますが、それは関数が結果を出せないという意味ではないことを期待しています。

ブラウザの現在の世代のJavascriptには、他の実行を許可するwait()またはsleep()がありません。ですから、あなたは単にあなたが求めていることをすることができません。代わりに、(約束を使用してきたように)非同期操作があり、それらの処理を実行し、処理が完了したら呼び出します。

これの一部は、Javascriptの単一のスレッド化によるものです。単一のスレッドが回転している場合、その回転するスレッドが完了するまで、他のJavascriptは実行できません。 ES6にはyieldとジェネレーターが導入されており、そのような協調的なトリックが可能になりますが、インストールされたブラウザーの幅広い見本でそれらを使用することはできません(JSを制御するサーバー側の開発で使用できます)使用されているエンジン)。


Promiseベースのコードを慎重に管理することで、多くの非同期操作の実行順序を制御できます。

コードでどのような順序を達成しようとしているのか正確にはわかりませんが、既存の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年後):

asyncawaitなどのES7機能をサポートする環境でコードをトランスパイルするか、コードを実行する場合、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
});
53
jfriend00

ES2016を使用している場合は、asyncおよびawaitを使用して、次のようなことができます。

(async () => {
  const data = await fetch(url)
  myFunc(data)
}())

ES2015を使用している場合は、 Generators を使用できます。構文が気に入らない場合は、 asyncユーティリティ関数 as ここで説明 を使用して抽象化できます。

最後に、ES5を使用している場合は、 Bluebird のようなライブラリを使用して、より細かく制御できるようにします。

8
Josh Habdas

もう1つのオプションは、Promise.allを使用して、Promiseの配列が解決するのを待つことです。以下のコードは、すべての約束が解決するのを待ち、すべての準備ができたら結果を処理する方法を示しています(質問の目的と思われるため)。ただし、resolveを呼び出す前にコードを追加するだけで、middleが解決する前にstartが出力されることは明らかです。

function kickOff() {
  let start = new Promise((resolve, reject) => resolve("start"))
  let middle = new Promise((resolve, reject) => {
    setTimeout(() => resolve(" middle"), 1000)
  })
  let end = new Promise((resolve, reject) => resolve(" end"))

  Promise.all([start, middle, end]).then(results => {
    results.forEach(result => $("#output").append(result))
  })
}

kickOff()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>
3
Stan Kurdziel