链接Promises(.animate和setTimeout之间的差异)

Mat*_*ava 3 html javascript jquery promise

我想问一下javascript中的承诺是如何运作的.我很难理解由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);
});
Run Code Online (Sandbox Code Playgroud)

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

promise.then(function(data) {
    setTimeout(function() {
        console.log("timeout started");
    }, 1000);
});
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:为什么第一种情况正常,第二种情况不正常?我认为异步任务以相同的方式处理.我应该如何更改代码以正确链接任何函数(等待先前的函数执行).

jfr*_*d00 8

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

所以,如果你想让你的两个动画一个接一个,你可以简单地做到这一点而不使用任何承诺:

 $("#something").animate({width: 100}, 1000).animate({height: 100}, 1000);
Run Code Online (Sandbox Code Playgroud)

然后,如果你想使用jQuery动画的promises,你必须获得动画的承诺,如下所示:

$("#something").animate({width: 100}, 1000).promise();
Run Code Online (Sandbox Code Playgroud)

然后你可以使用那个承诺.then().

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

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

只是打电话:

$("#something").animate({width: 100}, 1000);
Run Code Online (Sandbox Code Playgroud)

在您的原始代码中返回一个jQuery对象,这不是一个promise本身可以使用的东西.


如果你真的想创建自己的promise对象并自己解决它(jQuery动画不需要的东西,当你已经有一个可以使用的承诺时不推荐),你可以这样做,你可以使用完成函数动画来解决你的承诺(使用你的编码风格):

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);
});
Run Code Online (Sandbox Code Playgroud)

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

然后,当您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
});
Run Code Online (Sandbox Code Playgroud)

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