使用promises迭代同步ajax调用

时间:2017-08-14 21:24:55

标签: javascript jquery ajax promise synchronous

我目前有一些看起来有点像这样的jQuery代码:

for ( i = 0; i < limitVar; i++ ) {
doAjaxStuff(i);
}

function doAjaxStuff( i ) {
Here we make a SYNCHRONOUS ajax call, sending i.
}

ajax调用需要是同步的 - 在最后一个调用完成之前不会触发。

由于不推荐使用同步JS,我想将此代码移动到使用promises。我怎么做到这一点?我一直无法找到足够接近这种情况的例子。

3 个答案:

答案 0 :(得分:1)

你不能在浏览器中做同步ajax(从技术上讲,你可以在某些情况下,但这样做是一个非常糟糕的主意,因为它在ajax调用期间锁定了浏览器)。

相反,你重新设计你的循环,以便它只在前一个完成时执行下一个ajax调用,这意味着你必须手动循环,你不能使用for循环。由于您的代码是伪代码(您不显示真正的ajax操作),我将使用jQuery ajax示例,但您可以替换任何ajax函数,只要它返回一个promise或者在完成时使用回调来发出信号。

一般的想法是你为ajax调用创建一个函数,然后使用完成回调来递增索引,然后运行循环的下一次迭代。

function runLoop(data) {
    var i = 0;

    function next() {
        if (i < data.length) {
            return $.ajax(data[i]).then(function(data) {
                ++i;
                return next();
            });
         else {
             // all done with loop
         }
    }
    return next();
}

// call it like this
runLoop(someArray).then(function() {
   // all done here
});

如果您没有数据数组,但只想要一个循环索引:

function runLoop(limitVar) {
    var i = 0;

    function next() {
        if (i < limitVar) {
            return $.ajax(something_with_i_in_it).then(function(data) {
                ++i;
                return next();
            });
         else {
             // all done with loop
         }
    }
    return next();
}

// call it like this
runLoop(theLimit).then(function() {
   // all done here
});

如果你的limitVar不大并且决定是否继续循环没有其他逻辑,如果你有一个返回一个promise的ajax函数,你也可以使用一点点简单的模式:

function runLoop(limitVar) {
    var p = Promise.resolve();
    for (var i = 0; i < limitVar; i++) {
        p = p.then(function(prevResult) {
            return someAjax(i);
        });
    }
    return p;
}

// call it like this
runLoop(theLimit).then(function() {
   // all done here
});

如果你没有使用返回promise的ajax函数,那么只需几行代码就可以用你的函数包装你的函数,然后你可以更容易地使用这些设计模式。 / p>

答案 1 :(得分:0)

使用一些单独的函数处理数组。每次从数组中取出另一个元素,然后处理它,完成后再次调用该函数。如果列表中没有其他项目,则整个过程完成。

var listOfRequests = ...;

new Promise( function( resolve, reject ) {
   requestNext();

   function requestNext() {
       if ( !listOfRequests.length ) {
           return resolve();
       }

       var next = listOfRequests.shift();

       doAjaxStuff( next, reject, requestNext );
   }
} )

doAjaxStuff( request, errCallback, doneCallback ) {
    ...
}

答案 2 :(得分:-1)

这是一个非常简单的模式:

$$

或者如果您使用数组作为输入:

var queue = Promise.resolve();
var nop = () => null;

for(let i=0; i<limitVar; ++i){
  queue = queue.then(() => doAjaxStuff(i));
  //or if you want to ignore Errors
  //queue = queue.then(() => doAjaxStuff(i)).catch(nop);
}

queue.then(() => console.log("finished"));