以正确的顺序执行 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();
});
}
我正在编写一个程序,它接受一个句子并用数据库中的完整术语替换缩写(例如,"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()
中结果的处理。在这种情况下,无论如何您都希望 returndata.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();
});
}