How to make decisions based on return value of asy

2019-09-19 20:06发布

问题:

Node.js

while(list != []) {
  apiCall.then(function(data){
    list = data;
  });

}

where apiCall is a promise constructed like :

return new Promise(function (fulfill, reject){
        request("url", function (error, response, body){
            try {
                fulfill(body);
            } catch (error) {
                reject(error);
            }
        }, reject);
    }); 

Because the api calls are asynchronous, something goes wrong and the loop never ends. How can I fix this issue ?

回答1:

You can run functions with callbacks inside the loops synchronously using SynJS. Here is a working code to illustrate:

var SynJS = require('synjs');
var request = require('request');

function myFunction1(modules) {
    var list, i=0;
    while(i<5) {
        modules.request("http://www.google.com", function (error, response, body){
            list = body;
            console.log("got it!", i, new Date());
            modules.SynJS.resume(_synjsContext); //<-- indicates that callback is finished
        });
        SynJS.wait(); //<-- wait for callback to finish
        i++;
    };
};

var modules = {
        SynJS:  SynJS,
        request:    request,
};

SynJS.run(myFunction1,null,modules,function (ret) {
    console.log('done');
});

And here is a result:

got it! 0 Thu Jan 05 2017 18:17:20 GMT-0700 (Mountain Standard Time)
got it! 1 Thu Jan 05 2017 18:17:20 GMT-0700 (Mountain Standard Time)
got it! 2 Thu Jan 05 2017 18:17:21 GMT-0700 (Mountain Standard Time)
got it! 3 Thu Jan 05 2017 18:17:21 GMT-0700 (Mountain Standard Time)
got it! 4 Thu Jan 05 2017 18:17:21 GMT-0700 (Mountain Standard Time)
done


回答2:

You can't use a synchronous while loop waiting for an async result. The asynchronous callback and, in this case, the .then() handler will NEVER get to execute. You just can't program single-threaded Javascript this way. The interpreter just keeps running your while loop forever and though events may be piling up in the event queue to trigger async callbacks, those events never get serviced because your while loop never stops. You just can't program async behavior this way in an event driven, single threaded environment like Javascript is.

Instead, you need to NOT use synchronous looping. The typical solution involves making the async call, evaluating the result. If you want to execute the async function again at that point, you make a function call to execute it again.

function runIt() {
     return a().then(function() {
         if (needToRunAgain) {
             return runIt();
         } else {
             return someValue;
         }
     });
}

This will call the async operation again if conditions desire that and will chain the resulting promise to the original promise, allowing the caller to know exactly when the result is finally done. You then invoke the code like this:

runIt(...).then(function(result) {
    // result here
    // you must use the async result here or call a function and pass the result
    // to it.  You cannot assign it to a higher scoped variable and expect 
    // other code that follows to be able to use it.
}, function(err) {
    error here
});