[英]Refactor nested promises to a chain of promises
I have simplified this problem.我已经简化了这个问题。 I have 3 functions which I want to run with a 2 second delay between each.
我有 3 个函数,我想在每个函数之间运行 2 秒延迟。 The following code is working:
以下代码正在工作:
$.when(one()).done(function () {
$.when(delay(2000)).done(function () {
$.when(two()).done(function () {
$.when(delay(2000)).done(function () {
$.when(three()).done(function () {
console.log('finished');
});
});
});
});
});
function delay(ms) {
var waitForDelay = new $.Deferred();
setTimeout(function () {
waitForDelay.resolve().promise();
}, ms);
return waitForDelay.promise();
}
function one() {
console.log('one');
return new $.Deferred().resolve().promise();
}
function two() {
console.log('two');
return new $.Deferred().resolve().promise();
}
function three() {
console.log('three');
return new $.Deferred().resolve().promise();
}
However when I try to refactor it, it no longer waits for the delayed amounts of time:但是,当我尝试重构它时,它不再等待延迟的时间:
one().done(delay(2000)).done(two).done(delay(2000)).done(three).done(function () {
console.log('finished');
});
How can I refactor to chain these promises rather than nesting them?我如何重构以链接这些承诺而不是嵌套它们? I want to use jQuery for older browser compatibility.
我想使用 jQuery 来实现旧版浏览器的兼容性。
My advice would be to not use jQuery's weird promise-like data structures at all.我的建议是根本不要使用 jQuery 奇怪的类似Promise 的数据结构。
Have your functions return an actual Promise (either explicitly or by making them async functions ) and await
each step in the chain.让您的函数返回一个实际的Promise (显式地或通过使它们成为异步函数)并
await
链中的每个步骤。
const delay = (ms) => new Promise((res) => setTimeout(res, ms)); // an async function implicitly returns a promise const one = async () => { console.log("one"); } // or you can return one manually const two = () => { console.log("two"); return Promise.resolve(); } const three = async () => { console.log("three"); } // await each step in an async function (an async IIFE in this case) (async () => { await one(); await delay(2000); await two(); await delay(2000); await three(); console.log("finished"); })();
You can also use .then()
if you prefer that style如果您喜欢这种风格,也可以使用
.then()
// Convenience function so you don't repeat yourself
const waitTwoSeconds = () => delay(2000);
one()
.then(waitTwoSeconds)
.then(two)
.then(waitTwoSeconds)
.then(three)
.then(() => {
console.log("finished");
});
If you must use jQuery, it doesn't really look much different如果你必须使用 jQuery,它看起来并没有太大的不同
function delay(ms) { var d = $.Deferred(); setTimeout(d.resolve, ms); return d.promise(); } function one() { console.log("one"); return $.Deferred().resolve().promise(); } function two() { console.log("two"); return $.Deferred().resolve().promise(); } function three() { console.log("three"); return $.Deferred().resolve().promise(); } function waitTwoSeconds() { return delay(2000); } one() .then(waitTwoSeconds) .then(two) .then(waitTwoSeconds) .then(three) .then(function() { console.log("finished"); });
<!-- Note jQuery 1.8 was the first to have chainable deferreds --> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
I've used .then()
instead of .done()
because the latter does not chain and using $.when()
is unnecessary when .then()
is available.我使用了
.then()
而不是.done()
,因为后者没有链接并且在.then()
可用时使用$.when()
是不必要的。
Taking inspiration from Phil's answer, here is a solution compatible with older browsers.从 Phil 的回答中汲取灵感,这里有一个与旧浏览器兼容的解决方案。
First, change the delay()
function to return a function:首先,将
delay()
函数更改为返回一个函数:
function delay(ms) {
return function() {
var waitForDelay = new $.Deferred();
setTimeout(function () {
waitForDelay.resolve().promise();
}, ms);
return waitForDelay.promise();
}
}
All other functions stay the same as in the question.所有其他功能与问题中的相同。
Finally replace the .done()
s with .then()
s to make them chain:最后将
.done()
替换为 .then( .then()
以使它们链接:
one().then(delay(2000)).then(two).then(delay(2000)).then(three).then(function () {
console.log('finished');
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.