Asynchronous for cycle in JavaScript

前端 未结 13 1100
温柔的废话
温柔的废话 2020-11-22 11:37

I need a loop that waits for an async call before continuing. Something like:

for ( /* ... */ ) {

  someFunction(param1, praram2, function(result) {

    //         


        
相关标签:
13条回答
  • 2020-11-22 11:44

    You can use async await introduced in ES7:

    for ( /* ... */ ) {
        let result = await someFunction(param1, param2);
    }
    alert("For cycle ended");
    

    This works only if someFunction is returning a Promise!

    If someFunction is not returning a Promise, then you can make it return a Promise by yourself like this:

    function asyncSomeFunction(param1,praram2) {
      return new Promise((resolve, reject) => {
        someFunction(praram1,praram2,(result)=>{
          resolve(result);
        })
      })
    }
    

    Then replace this line await someFunction(param1, param2); by await asynSomeFunction(param1, param2);

    Please understand Promises before writing async await code!

    0 讨论(0)
  • 2020-11-22 11:44

    A promise library based solution:

    /*
        Since this is an open question for JS I have used Kris Kowal's Q promises for the same
    */
    
    var Q = require('q');
    /*
        Your LOOP body
        @success is a parameter(s) you might pass
    */
    var loopBody = function(success) {
        var d = Q.defer(); /* OR use your favorite promise library like $q in angular */
        /*
            'setTimeout' will ideally be your node-like callback with this signature ... (err, data) {}
            as shown, on success you should resolve 
            on failure you should reject (as always ...) 
        */
        setTimeout(function(err, data) {
            if (!err) {
                d.resolve('success');
            } else {
                d.reject('failure');
            }
        }, 100); //100 ms used for illustration only 
        return d.promise;
    };
    
    /*
        function to call your loop body 
    */
    function loop(itr, fn) {
        var def = Q.defer();
        if (itr <= 0) {
            def.reject({ status: "un-successful " });
        } else {
            var next = loop.bind(undefined, itr - 1, fn); // 'next' is all there is to this 
            var callback = fn.bind(undefined /*, a, b, c.... */ ); // in case you want to pass some parameters into your loop body
            def.promise = callback().then(def.resolve, next);
        }
        return def.promise;
    }
    /*
        USAGE: loop(iterations, function(){})
        the second argument has to be thenable (in other words return a promise)
        NOTE: this loop will stop when loop body resolves to a success
        Example: Try to upload file 3 times. HURRAY (if successful) or log failed 
    */
    
    loop(4, loopBody).then(function() {
        //success handler
        console.log('HURRAY')
    }, function() {
        //failed 
        console.log('failed');
    });
    
    0 讨论(0)
  • 2020-11-22 11:45

    If you like wilsonpage's answer but are more accustomed to using async.js's syntax, here is a variation:

    function asyncEach(iterableList, callback, done) {
      var i = -1,
          length = iterableList.length;
    
      function loop() {
          i++;
          if (i === length) {
            done(); 
            return;
          }
          callback(iterableList[i], loop);
      } 
      loop();
    }
    
    
    asyncEach(['A', 'B', 'C'], function(item, callback) {
        setTimeout(function(){
        document.write('Iteration ' + item + ' <br>');
        callback();
      }, 1000);
    }, function() {
      document.write('All done!');
    });
    

    Demo can be found here - http://jsfiddle.net/NXTv7/8/

    0 讨论(0)
  • 2020-11-22 11:46

    Also look at this splendid library caolan / async. Your for loop can easily be accomplished using mapSeries or series.

    I could post some sample code if your example had more details in it.

    0 讨论(0)
  • 2020-11-22 11:46

    I needed to call some asynchronous function X times, each iteration must have happened after the previous one was done, so I wrote a litte library that can be used like this:

    // https://codepen.io/anon/pen/MOvxaX?editors=0012
    var loop = AsyncLoop(function(iteration, value){
      console.log("Loop called with iteration and value set to: ", iteration, value);
    
      var random = Math.random()*500;
    
      if(random < 200)
        return false;
    
      return new Promise(function(resolve){
        setTimeout(resolve.bind(null, random), random);
      });
    })
    .finished(function(){
      console.log("Loop has ended");
    });
    

    Each time user defined loop function is called, it has two arguments, iteration index and previous call return value.

    This is an example of output:

    "Loop called with iteration and value set to: " 0 null
    "Loop called with iteration and value set to: " 1 496.4137048207333
    "Loop called with iteration and value set to: " 2 259.6020382449663
    "Loop called with iteration and value set to: " 3 485.5400568702862
    "Loop has ended"
    
    0 讨论(0)
  • 2020-11-22 11:51

    Given an asynchronous worker function someFunction that will call back a result function with a result argument saying whether or not the loop should continue:

    // having:
    // function someFunction(param1, praram2, resultfunc))
    // function done() { alert("For cycle ended"); }
    
    (function(f){ f(f) })(function(f){
      someFunction("param1", "praram2", function(result){
        if (result)
          f(f); // loop continues
        else
          done(); // loop ends
      });
    })
    

    In order to check whether or not to end the loop, the worker function someFunction can forward the result function to other asynchronous operations. Also, the whole expression can be encapsulated into an asynchronous function by taking a function done as callback.

    0 讨论(0)
提交回复
热议问题