【问题标题】:Iterating over synchronous ajax calls with promises使用 Promise 迭代同步 ajax 调用
【发布时间】:2017-08-14 21:24:55
【问题描述】:

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

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

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

ajax 调用需要同步——直到最后一个调用完成后才会触发。

由于不推荐使用同步 JS,我想移动此代码以使用 Promise。我将如何实现这一目标?我一直找不到一个足够接近这种情况的例子。

【问题讨论】:

  • @KevinB Promise.all() 正在一次触发所有请求。如果数组足够大,那肯定会耗尽资源导致请求失败。
  • 您可以使用 [].reduce 创建一个依次调用它们的链。例如:stackoverflow.com/questions/21372320/…
  • 迁移到 Promise 意味着迁移到异步。

标签: javascript jquery ajax promise synchronous


【解决方案1】:

您不会在浏览器中执行同步 ajax(从技术上讲,在某些情况下您可以,但这样做真的很糟糕,因为它会在 ajax 调用期间锁定浏览器)。

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

一般的想法是,您为 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 函数,那么只需几行代码就可以将您的函数包装成这样的函数,然后您就可以更轻松地使用这些设计模式。

【讨论】:

  • 为什么投反对票?这个答案有什么问题?只有在您提供反馈以说明反对意见被否决的原因时,人们才能改进他们的答案以解决反对意见。
【解决方案2】:

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

var listOfRequests = ...; 新的承诺(功能(解决,拒绝){ 请求下一个(); 函数请求下一个(){ 如果(!listOfRequests.length){ 返回解析(); } var next = listOfRequests.shift(); doAjaxStuff(next, reject, requestNext); } }) doAjaxStuff(请求,errCallback,doneCallback){ ... }

【讨论】:

    【解决方案3】:

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

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

    或者如果你使用一个数组作为输入:

    var done = data.reduce(
        (queue, value, index) => queue.then(() => doSomethingWith(value, index)), 
        Promise.resolve()
      );
    
    done.then(() => console.log("finished"));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多