【问题标题】:chaining Promises (difference between .animate and setTimeout)链接承诺(.animate 和 setTimeout 之间的区别)
【发布时间】:2014-07-03 16:46:06
【问题描述】:

我想问一下 javascript 中的 Promise 究竟是如何工作的。我很难理解由 jQuery.animate 和 setTimeout 组成的链的执行之间的区别。

如果我这样做:

var promise = new Promise(function(resolve, reject) {
    resolve(
        $("#something").animate({
            width: 100
        }, 1000);
    )
});

promise.then(function(data) {
    $("#something").animate({
        height: 100
    }, 1000);
});

然后第二个动画在第一个动画完成后开始(这是想要的行为)。另一方面,当我将.then(替换.animate)更改为例如setTimeout 然后在动画开始后立即执行(它不等待动画结束)。

promise.then(function(data) {
    setTimeout(function() {
        console.log("timeout started");
    }, 1000);
});

所以我的问题是:为什么第一个场景可以正常工作而第二个不行?我认为异步任务的处理方式相同。以及我应该如何更改代码以正确链接任何函数(等待前面的函数执行)。

【问题讨论】:

    标签: javascript jquery html promise


    【解决方案1】:

    首先,jQuery 将通过其内置的动画队列为您序列化同一对象上的动画。你不需要使用任何 Promise 来做到这一点。

    因此,如果您希望两个动画一个接一个,您可以简单地执行此操作,而无需使用任何承诺:

     $("#something").animate({width: 100}, 1000).animate({height: 100}, 1000);
    

    然后,如果你想在 jQuery 动画中使用 Promise,你必须像这样获取动画的 Promise:

    $("#something").animate({width: 100}, 1000).promise();
    

    然后你可以将这个承诺与.then() 一起使用。

    如果你想使用 jQuery Promise 来控制动画,你可以这样做:

    $("#something").animate({width: 100}, 1000).promise().then(function() {
        $("#something").animate({height: 100}, 1000);
    });
    

    只是打电话:

    $("#something").animate({width: 100}, 1000);
    

    在您的原始代码中返回一个 jQuery 对象,它不是 Promise 可以单独使用的对象。


    如果你真的想创建自己的 Promise 对象并自己解决它(jQuery 动画不需要,并且在已经有可以使用的 Promise 时不推荐使用),你可以在使用动画中的完成函数来解决您的承诺(使用您的编码风格):

    var promise = new Promise(function(resolve, reject) {
        $("#something").animate({width: 100}, 1000, resolve);
    });
    
    promise.then(function(data) {
        console.log("timeout started");
        setTimeout(function() {
            console.log("timeout finished");
        }, 1000);
    });
    

    所以,现在解释一下您的代码中发生了什么。在您的第一个示例中,您的承诺逻辑根本不起作用(它几乎只是编码错误)。因此,两个动画都被立即调用,但是因为 jQuery 将它们排队以便为您按顺序运行,这就是您看到的行为。排序与您违反承诺的逻辑无关。

    然后,当您为第二个操作输入setTimeout() 时,jQuery 不再为您排队,因为它本身不是 jQuery 动画,并且您的承诺逻辑仍然被破坏,因此两个操作同时执行。


    如果您真的只想链接超时,那么您可以制作一个将超时与承诺相结合并且可以链接的包装器:

    function timer(t) {
        return new Promise(function(resolve, reject) {
            setTimeout(function() {
                resolve();
            }, t);
        });
    }
    
    timer(1000).then(function() {
        // do something here
        return timer(1000);
    }).then(function() {
        // do something here
    });
    

    工作演示:http://jsfiddle.net/jfriend00/85Pr6/(需要内置 Promise 的浏览器)

    【讨论】:

    • 感谢您的回复,但我看不出这是如何回答我的问题的。
    • @MatúšDúbrava - 我现在已经解释了为什么你的代码没有工作,除了告诉你如何让它工作。
    • 现在它可以工作了,但我仍然有这个问题,如果我用例如替换promise对象的内容setTimeout (chain 2 timeouts) 像这样,那么它就不起作用了。据我了解,.then 被放入该动画的回调中,但它不适用于超时。
    • @MatúšDúbrava - 对不起,但我不明白你想要做什么 - 你需要展示你正在尝试的实际代码。您只能链接返回在操作完成时解决的承诺的事物。您不能像setTimeout() 这样链接多个普通函数调用并期望它们序列化。 Promise 没有什么魔法可以知道它们的操作何时完成。它们通过一系列回调工作,其中的操作必须解决承诺,当它返回时,它可以链接到另一个承诺操作。
    • 这就是我所缺少的,我希望我可以将 2 次超时与承诺联系起来。
    猜你喜欢
    • 2018-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-20
    • 2017-04-26
    • 2015-06-06
    • 2023-03-14
    • 2017-01-25
    相关资源
    最近更新 更多