【问题标题】:Programmatically creating a chained sequence of jQuery promises以编程方式创建 jQuery 承诺的链式序列
【发布时间】:2013-11-29 14:25:40
【问题描述】:

我有一个函数来删除一个列表文件,filePaths,方法是对列表中的每个文件调用一个 deleteFile(filePath)(我使用的一些 APIS 不支持批量删除)。函数 deleteFile 返回一个 jQuery 承诺,并在删除文件时解析/拒绝。

function deleteFiles(filePaths)
    var deferreds = $.map(fileKeys, function (val, index) {
        return deleteFile(filePath);
    });

    $.when.apply($, deferreds).then(function (schemas) {
        console.log("DONE", this, schemas);
        deferred.resolve();
    }, function (error) {
        console.log("My ajax failed");
        deferred.reject(error);
    });

我正在对列表中的某些文件进行 .reject 调用(我知道它们存在),所以我想我可能需要将 filePaths 数组转换为调用链,例如队列(b /c 这不是 $.when 所做的,是吗?它似乎一次启动它们)。我知道如何做到这一点(比如 .deleteFile(path1).deletePath(path2). etc 当它们在这样的数组中时。

提前感谢任何帮助。

【问题讨论】:

  • 即使其中一个 ajax 请求失败了,那么 $.when 也会调用拒绝处理程序...如果失败了你想做什么

标签: jquery asynchronous promise jquery-deferred


【解决方案1】:

$.when 没有启动任何东西,它们是在您的地图循环中启动的。 $.when 简单地返回一个承诺数组的承诺。

如果您希望它们按顺序排列,请使用 reduce:

function deleteFiles(filePaths) {
    return filePaths.reduce(function(cur, next) {
         return cur.then(function() {
             return deleteFile(next);
         });
    }, $().promise());
}

如果您希望它们按顺序排列,同时还可以使用相应的结果返回数组:

function deleteFiles(filePaths) {
    var ret = filePaths.slice(0);
    return filePaths.reduce(function(cur, next, i) {
         return cur.then(function() {
             return ret[i] = deleteFile(next);
         });
    }, $().promise()).then(function(){
         return $.when.apply($, ret);
    })
    //These don't make any sense to call in this function but sure
    .then(function(schemas) {
         console.log("DONE", this, schemas);
    }).fail(function(error) {
         console.log("My ajax failed");
    });
}

【讨论】:

  • 太棒了,非常感谢。我从未遇到过数组 reduce 方法,并且仍在阅读它的工作原理,但是您的第一个代码 sn-p 似乎可以工作,所以我很亲切。
【解决方案2】:

这不是 $.when 所做的,是吗?似乎将它们全部启动 一次

新的更新答案: 我刚刚发现对于新版本的 jQuery (>1.8),我们可以简单地使用 $.then 链接 jQuery Promise。在您的情况下,您可以尝试:

function deleteFiles(filePaths){
    var d = jQuery.Deferred(), 
    for (var i=0;i<filePaths.length;i++){
        d.then(deleteFile(filePath[i]));
    }

    d.resolve();
}

你可以看看我在jQuery Deferred and Promise for sequential execution of synchronous and asynchronous funcitons的类似回答

旧答案:

据我了解,您需要逐个处理返回值,我认为您不需要关心执行回调的顺序,因为下一个deleteFile 不需要取决于前一个的结果。如果是这种情况,试试这个:

 function deleteFiles(filePaths){
        $.each(filePaths,function(index,val){
              deleteFile(val)
                .done(function(schema){

                })
                .fail(function(error){

                });
        });
   }

如果需要返回延迟,另一种写法:

function deleteFiles(filePaths)
    return $.map(fileKeys, function (val,index) {
        return deleteFile(val);
    });
}

$.each(deleteFiles(yourfilePaths),function(index,val){
    val
    .done(function(schema){
    })
    .fail(function(error){
    });
});

如果你确实需要链接它们,我认为你可以创建一个这样的递归函数:

function deleteFiles(filePaths, currentIndex){
    if (filePath.length < currentIndex + 1){ //Stop condition.
        return;
    }

    var promise = deleteFile(filePath[currentIndex]);
    if (promise){
        promise
         .done(function(schema){
            //do your job with returned value, decide whether to call the next path in the chain
            deleteFiles(filePaths,currentIndex++);
         })
         .fail(function(error){
              //I assume that you don't call the next path if the current one returns error.
         });
    }
}

【讨论】:

  • 感谢您的建议。我确实需要将它们链接起来 b/c 我正在进行的调用是对文件系统的,我相信我的个人承诺开始失败的原因是 b/c 太多的同时文件访问,所以我需要按顺序进行。与递归函数相比,我更喜欢 reduce 解决方案,因为它更简洁一些。感谢您的回答。
  • @Bradley Bossard:使用 $.then 尝试我刚刚发现的新解决方案,这使得代码非常简单。
猜你喜欢
  • 2013-09-15
  • 1970-01-01
  • 2021-02-27
  • 1970-01-01
  • 1970-01-01
  • 2017-04-26
  • 2013-03-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多