承诺未在.each()循环中解决

时间:2016-08-04 10:20:52

标签: javascript jquery promise jquery-deferred

我必须运行一个递归过程,并且承诺不能按我的意愿运行。这是代码:

var openAllLeves = function () {
    openAll = 1;
    $.when(openLevels()).then(openAll = 0);
}

var openLevels = function () {
    var promises = [];     
    $('.myClass:not([data-type="T"])').each(function () {
        var defer = $.Deferred();
        $.when(loadLine($(this)).then(promises.push(defer)));
    });
    return $.when.apply(undefined, promises).promise();
}

var loadLine = function (thisObj) {
    var defer = $.Deferred();
    switch(nivel) {
        case 1:
            $.when(getPT($(thisObj).attr('data-a'))).then(defer.resolve());
            break;
        case 2:
            // ...
    }
    return defer.promise();
}

var getPT = function (psn) {
    var defer = $.Deferred();
    var payload = { /* parameters... */ };
    $.ajax({
        url: webmethod,
        data: payload,
        type: "POST",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        timeout: 10000,
        success: function (data) {
            $.when(paintPT(data)).then(function () { 
                if (openAll) 
                    openLevels(), defer.resolve(); 
            });
        }
    });
    return defer.promise();
}

我的问题是openAll的值在ajax函数成功代码中被评估之前变为0,因此只执行一次迭代并且没有完成递归。看起来像.then是在解析promises数组之前执行的。

代码有点令人困惑,所以任何帮助都表示赞赏。 提前谢谢。

3 个答案:

答案 0 :(得分:1)

代码中的一个大问题是,您正在调用然后回调中的函数,而不是将它们传递给它。例如:

.then(defer.resolve());

这样,您将 defer.resolve()的值传递给 then 回调,而不是异步操作完成时应调用的函数。你应该做这样的事情:

.then(defer.resolve.bind(defer));

这同样适用于其余代码。 你应该看一下承诺spec

特别是

  

如果onFulfilled不是函数,则必须忽略它。

修改

正如Bergi所指出的那样,你应该避免推迟反模式。

答案 1 :(得分:1)

避免使用deferred antipattern

此外,当您将某些内容传递给.then()时,它必须是回调函数,调用promises.push(defer)defer.resolve()openAll = 0左右不起作用,它会执行该操作立即表达,而不是等待承诺。

$.when().promise()来电几乎是多余的。放下它们。

function openAllLeves () {
    openAll = 1;
    openLevels().then(function() {
        openAll = 0
    });
}

function openLevels() {
    var promises = [];     
    $('.myClass:not([data-type="T"])').each(function () { // using `map` would be even better
        promises.push(loadLine($(this)));
    });
    return $.when.apply($, promises);
}

function loadLine(thisObj) {;
    switch(nivel) {
        case 1:
            return getPT($(thisObj).attr('data-a'))
        case 2:
            // ...
    }
}

function getPT(psn) {
    var payload = { /* parameters... */ };
    return $.ajax({
        url: webmethod,
        data: payload,
        type: "POST",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        timeout: 10000,
    }).then(function (data) {
        return paintPT(data);
    }).then(function () { 
        if (openAll) 
           openLevels();
    });
}

顺便说一句,您可能希望将if (openAll) openLevels();链接到openLevels()的返回值,而不是链接到每个请求承诺。

答案 2 :(得分:0)

感谢您的回复。我正在研究这种变化。这样,我明白.then()只在传递函数时等待promise。因此,在.then()中解决承诺的正确方法是......

.then(function() {
  defer.resolve();
})

¿?