web-dev-qa-db-ja.com

JavaScript:whileループの非同期メソッド

APIメソッド呼び出しでJavaScriptを使用する必要があるプロジェクトに取り組んでいます。私はJavaプログラマーで、これまでにWeb開発を行ったことがないため、問題が発生しています。

このAPIメソッドは非同期であり、whileループ内にあります。空の配列を返す場合、whileループは終了します。それ以外の場合、ループします。コード:

var done = true;

do
{
    async_api_call(
        "method.name", 
        { 
            // Do stuff.
        },
        function(result) 
        {
            if(result.error())
            {
                console.error(result.error());
            }
            else
            {
                // Sets the boolean to true if the returned array is empty, or false otherwise.
                done = (result.data().length === 0) ? true : false;
            }
        }
    );

} while (!done);

これは機能しません。 「完了」の値が更新される前にループが終了します。私は主題についていくつか読んでいますが、API呼び出しは非同期であるため、約束またはコールバックを使用する必要があるようですが、上記のコードにそれらを適用する方法を理解できません。

ヘルプをいただければ幸いです!

8
Step

edit:一番下を見てください、本当の答えがあります。

Promise API を使用することをお勧めします。問題はPromise.all呼び出しを使用して解決できます。

let promises = [];
while(something){
    promises.Push(new Promise((r, j) => {
        YourAsyncCall(() => r());
    });
}
//Then this returns a promise that will resolve when ALL are so.
Promise.all(promises).then(() => {
    //All operations done
});

構文はes6にあります。これはes5に相当するものです(Promise APIは外部に含まれる場合があります)。

var promises = [];
while(something){
    promises.Push(new Promise(function(r, j){
        YourAsyncCall(function(){ r(); });
    });
}
//Then this returns a promise that will resolve when ALL are so.
Promise.all(promises).then(function(){
    //All operations done
});

API呼び出しでpromiseを返して、promise配列に直接プッシュすることもできます。

api_call_methodを編集したくない場合は、いつでもコードを新しいプロミスでラップし、終了時にメソッドresolveを呼び出すことができます。

edit:私はあなたのコードのポイントを見ました、ごめんなさい。 Promise.allでは問題が解決しないことに気付きました。

投稿したもの(whileループと制御値を除く)を関数内に配置し、それを再度呼び出す条件に依存します。

その後、外部コードにこの非同期実行を認識させるために、すべてをプロミス内にラップできます。後でサンプルコードをPCに投稿します。

良い答え

Promiseを使用してアプリケーションのフローを制御し、whileループの代わりに再帰を使用できます。

function asyncOp(resolve, reject) {
    //If you're using NodeJS you can use Es6 syntax:
    async_api_call("method.name", {}, (result) => {
      if(result.error()) {
          console.error(result.error());
          reject(result.error()); //You can reject the promise, this is optional.
      } else {
          //If your operation succeeds, resolve the promise and don't call again.
          if (result.data().length === 0) {
              asyncOp(resolve); //Try again
          } else {
              resolve(result); //Resolve the promise, pass the result.
          }
      }
   });
}

new Promise((r, j) => {
    asyncOp(r, j);
}).then((result) => {
    //This will call if your algorithm succeeds!
});

/*
 * Please note that "(...) => {}" equivals to "function(...){}"
 */
4
SigmaSoldier

Promisesを使用したくない場合は、次のようにコードを再構築できます。

var tasks = [];
var index = 0;

function processNextTask()
{
    if(++index == tasks.length)
    {
        // no more tasks
        return;
    }

    async_api_call(
        "method.name", 
        { 
            // Do stuff.
        },
        function(result) 
        {
            if(result.error())
            {
                console.error(result.error());
            }
            else
            {
                // process data
                setTimeout(processNextTask);
            }
        }
    );
}
2
Hayko Koryun

また、再帰ソリューションを試すこともできます。

function asyncCall(cb) {
// Some async operation
}

function responseHandler(result) {
    if (result.error()) {
        console.error(result.error());
    } else if(result.data() && result.data().length) {
        asyncCall(responseHandler);
    }
}

asyncCall(responseHandler);
1
Roma Harevich

ループは同期され、非同期タスクは非同期であるため、ループは機能しません。非同期タスクが応答する前にループが終了します。 Promiseを使用して非同期タスクを管理することをお勧めします。

//first wrapping your API into a promise
var async_api_call_promise = function(methodName, someObject){
    return new Promise((resolve, reject) => {
        async_api_call(methodName, someObject, function(result){
            if(result.error()){ 
                reject( result.error() ) 
            }else{
                resolve( result.data() )
            }
        });
    })
}

今、あなたのポーリングコードに:

//a local utility because I don't want to repeat myself
var poll = () => async_api_call_promise("method.name", {/*Do stuff.*/});

//your pulling operation
poll().then(
    data => data.length === 0 || poll(),  //true || tryAgain
    err => {
        console.error(err);
        return poll();
    }
).then((done) => {
    //done === true
    //here you put the code that has to wait for your "loop" to finish
});

約束する理由非同期操作の状態管理を行うためです。なぜそれを自分で実装するのですか?

0
Thomas