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

Promises not resolved in a .each() loop

我必须 运行 一个递归过程,但 promise 没有按我的意愿工作。这是代码:

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 是在解析承诺数组之前执行的。

代码有点混乱,如有任何帮助,我们将不胜感激。 提前致谢。

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

.then(defer.resolve());

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

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

其余代码也是如此。 你应该看看承诺 spec

特别是

If onFulfilled is not a function, it must be ignored.

编辑

正如 Bergi 所指出的,您应该避免延迟反模式。

避免 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() 的 return 值,而不是每个请求承诺。

谢谢你们的回复。我正在研究这个变化。这样,我了解到 .then() 仅在传递函数时等待承诺。所以在 .then() 中解决承诺的正确方法是..

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

?