链接Promises(Promise链):.animate和setTimeout之间的区别

3

我想询问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);
});

我的问题是:为什么第一个情况能够顺利工作而第二个情况不能?我以为异步任务会以相同的方式处理。我应该如何更改代码以正确地链接任何函数(等待前面的函数执行)。

1个回答

8

首先,jQuery通过其内置的动画队列为您对同一对象序列化动画。您不需要使用任何承诺来完成此操作。

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

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

如果你想在jQuery动画中使用promises,你需要通过以下方式获取动画的promise:

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

然后,您可以使用该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时不建议这样做),您可以像以下示例一样使用动画的完成函数来解决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);
});

所以,现在来解释一下你的代码中发生了什么。在你的第一个例子中,你的promise逻辑根本没有起作用(基本上是编码错误)。因此,两个动画都会立即被调用,但由于jQuery为您顺序排队运行它们,这就是您看到的行为。排序与您的破碎的promise逻辑无关。
然后,当您为第二个操作放入setTimeout()时,jQuery不再为您排队,因为它本身不是jQuery动画,并且您的promise逻辑仍然存在问题,因此两个操作同时执行。
如果您真的想要仅链接超时,则可以创建一个将超时与promise组合在一起的包装器,这可以是可链接的:
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/(需要内置承诺的浏览器)


@MatúšDúbrava - 我已经解释了为什么你的代码没有工作,同时向你展示了如何使其工作。 - jfriend00
现在它可以工作了,但我仍然有这个问题,如果我用setTimeout(链接2个超时)之类的东西替换promise对象的内容,就像这样,那么它就不起作用了。从我的理解来看,.then被放置在动画的回调中,但对于超时操作它不起作用。 - Matúš Dúbrava
@MatúšDúbrava - 抱歉,我不理解你想做什么 - 你需要展示你正在尝试的实际代码。 你只能链接返回解决异步操作时的Promise的东西。 你不能链接多个普通函数调用,如 setTimeout() 并期望它们连续。 Promise 没有一些神奇的东西知道它们中的操作何时完成。 它们通过一系列回调工作,并且其中的操作必须解决Promise,并在返回时可以链接到另一个Promise操作。 - jfriend00
那就是我所缺少的,我希望能够使用Promise链接两个超时。 - Matúš Dúbrava
谢谢,我一直在寻找这样的东西。 - Matúš Dúbrava
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接