以正确的顺序执行 Promise 的 For 循环

Executing a For Loop of Promises in the Correct Order

我正在编写一个程序,它接受一个句子并用数据库中的完整术语替换缩写(例如,"hi and gm" --> "hello and good morning" 如果数据库中存储的缩写是 "hi" --> "hello" 和 "gm" --> "good morning")。原句存放在$('#translation').text()中,应相应替换。

所以我发现了如何让 getFullSentence() 等到 for 循环完成辅助函数 getFullWord(),通过使用 promises 来处理每个单词。

问题是 getFullWord() 没有按顺序执行,例如如果我记录 "i",我得到 0、3、1、2 而不是 0、1、2、3。我该如何解决这个问题?提前致谢!

function getFullSentence(uid, sentence) {
    var promises = [];

    var words = sentence.split(" ");
    $.each(words, function(i, word) {
        promises.push(getFullWord(uid, word, i));
    });

    $.when.apply(null, promises).done(function() { 
        play($('#translation').text());
    });
}

function getFullWord(uid, word, i) {
    var defer = $.Deferred();

    $.get("/checkAbbreviation/" + uid + "/" + word, function(data) {
        word = data.full;

        var currSentence = $('#translation').text() + " ";
        var newSentence = currSentence + word.toUpperCase();
        $('#translation').text(newSentence);    
    }).done(function() {
        defer.resolve();
    });

    return defer.promise();
}

这里有很多错误。首先,您不需要按顺序 运行 您的操作(就像您认为的那样)。你可以运行他们并行,让$.when()按顺序为你收集结果。这只需要您在收集完所有结果后最后进行所有处理,$.when() 将按顺序为您收集。

其次,您正在使用延迟反模式,在这种模式中,您围绕已经 return 承诺的内容创建承诺。您可以直接 return 来自 $.ajax() 的承诺,而不是创建一个新承诺。

其他一些改进:

  • 你可以用.map()代替.each().push()
  • 您可以使用 .join() 连接所有字符串。
  • 如果您 return 来自 $.ajax() 上的 .then() 处理程序的值,它会大大简化 $.when() 中结果的处理。在这种情况下,无论如何您都希望 return data.full.toUpperCase()
  • 仅使用 .then(),不使用 .done()
  • $.ajax() 中不要混用回调和承诺。使用其中之一(承诺可能是最好的)。

这是一种 运行 并行操作并按顺序收集结果的方法:

function getFullSentence(uid, sentence) {
    var promises = sentence.split(" ").map(function(word, i) {
        return getFullWord(uid, word, i);
    });

    $.when.apply($, promises).then(function() { 
        // get all results into an array
        var results = Array.prototype.slice.call(arguments);
        var text = results.join(" ");
        $('#translation').text(text);
        play(text);
    });
}

function getFullWord(uid, word, i) {
    return $.get("/checkAbbreviation/" + uid + "/" + word).then(function(data) {
        // make resolved value be data.full
        return data.full.toUpperCase();
    });
}