JavaScript: async method in while loop

I am on a project that requires me to use JavaScript with an API method call. I am a Java programmer who has never done web development, so I have problems with that.

This API method is asynchronous and is in a while loop. If it returns an empty array, the while loop ends. Otherwise, they are hinges. Code:

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

      

This does not work. The loop ends before the "done" value is updated. I have read this thread and it seems to me that I need to use promises or callbacks because the API call is asynchronous, but I cannot figure out how to apply them to the code I have above.

Help would be appreciated!

+3


source to share


4 answers


edit: look at the bottom, there is a real answer.

I encourage you to use the Promise API . Your problem can be solved by calling 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
});

      

The syntax is in es6, here's the es5 equivalent (the Promise API can be enabled externally):

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

      

You can also make your api call return a promise and pipe it directly to the promise array.

If you don't want to edit api_call_method, you can always wrap your code in a new promise and call the method when it finishes.



change . I saw your code is now, sorry. I just realized that Promise.all will not solve the problem.

You put what you put (excluding the while loop and watchdog) inside the function, and depending on the condition that calls it again.

Then everything can be tricked into a promise to make the external code aware of this asynchronous execution. I'll post some code examples from my PC.

So a good answer

You can use a promise to control the flow of your application and use recursion instead of a while loop:

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(...){}"
 */

      

+2


source


Also you can try recursive solution.



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

      

0


source


If you don't want to use Promises

, you can rearrange your code like this:

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

      

0


source


Your loop won't run because it is synchronized, your async task is asynchronous, so the loop will finish before the async task can respond. I recommend you use Promises to manage async tasks:

//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() )
            }
        });
    })
}

      

now for your poll code:

//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
});

      

Why Promises? Because they manage the state of asynchronous operations. Why implement this?

0


source







All Articles