简体   繁体   English

将嵌套的 Promise 重构为 Promise 链

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM