我应该如何调用3个函数来一个接一个地执行它们?

tex*_*xai 138 javascript closures asynchronous callback

如果我需要一个接一个地调用这个函数,

$('#art1').animate({'width':'1000px'},1000);        
$('#art2').animate({'width':'1000px'},1000);        
$('#art3').animate({'width':'1000px'},1000);        
Run Code Online (Sandbox Code Playgroud)

我知道在jQuery中我可以做类似的事情:

$('#art1').animate({'width':'1000px'},1000,'linear',function(){
    $('#art2').animate({'width':'1000px'},1000,'linear',function(){
        $('#art3').animate({'width':'1000px'},1000);        
    });        
});        
Run Code Online (Sandbox Code Playgroud)

但是,我们假设我没有使用jQuery而且我想调用:

some_3secs_function(some_value);        
some_5secs_function(some_value);        
some_8secs_function(some_value);        
Run Code Online (Sandbox Code Playgroud)

我应该如何调用这个函数来执行some_3secs_function,然后在该调用结束后,然后执行some_5secs_function并在该调用结束后再调用some_8secs_function

更新:

这仍然无效:

(function(callback){
    $('#art1').animate({'width':'1000px'},1000);
    callback();
})((function(callback2){
    $('#art2').animate({'width':'1000px'},1000);
    callback2();
})(function(){
    $('#art3').animate({'width':'1000px'},1000);
}));
Run Code Online (Sandbox Code Playgroud)

三个动画同时开始

我的错误在哪里

Pet*_*son 229

在Javascript中,有同步异步功能.

同步功能

Javascript中的大多数函数都是同步的.如果要连续调用多个同步函数

doSomething();
doSomethingElse();
doSomethingUsefulThisTime();
Run Code Online (Sandbox Code Playgroud)

他们将按顺序执行.doSomethingElse直到doSomething完成才会开始.doSomethingUsefulThisTime反过来,直到doSomethingElse完成才会开始.

异步功能

但是,异步功能不会相互等待.让我们看一下上面的代码示例,这次假设函数是异步的

doSomething();
doSomethingElse();
doSomethingUsefulThisTime();
Run Code Online (Sandbox Code Playgroud)

函数将按顺序初始化,但它们将大致同时执行.你无法一致地预测哪一个将首先完成:恰好在最短时间内执行的那个将首先完成.

但有时,您希望按顺序执行异步函数,有时您希望异步执行的函数异步执行.幸运的是,这可以分别使用回调和超时.

回调

让我们假设我们有,我们想以此,执行三项异步功能some_3secs_function,some_5secs_function以及some_8secs_function.

由于函数可以作为Javascript中的参数传递,因此可以在函数完成后将函数作为回调传递给执行.

如果我们创建这样的函数

function some_3secs_function(value, callback){
  //do stuff
  callback();
}
Run Code Online (Sandbox Code Playgroud)

然后你可以按顺序打电话,像这样:

some_3secs_function(some_value, function() {
  some_5secs_function(other_value, function() {
    some_8secs_function(third_value, function() {
      //All three functions have completed, in order.
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

超时

在Javascript中,您可以告诉函数在某个超时(以毫秒为单位)后执行.实际上,这可以使同步函数异步运行.

如果我们有三个同步函数,我们可以使用该setTimeout函数异步执行它们.

setTimeout(doSomething, 10);
setTimeout(doSomethingElse, 10);
setTimeout(doSomethingUsefulThisTime, 10);
Run Code Online (Sandbox Code Playgroud)

然而,这有点难看并且违反了DRY原则[维基百科].我们可以通过创建一个接受函数数组和超时的函数来清理它.

function executeAsynchronously(functions, timeout) {
  for(var i = 0; i < functions.length; i++) {
    setTimeout(functions[i], timeout);
  }
}
Run Code Online (Sandbox Code Playgroud)

这可以像这样调用:

executeAsynchronously(
    [doSomething, doSomethingElse, doSomethingUsefulThisTime], 10);
Run Code Online (Sandbox Code Playgroud)

总之,如果您要同步执行异步函数,请使用回调,如果您有要异步执行的同步函数,请使用超时.

  • @Peter - +1用于我所见过的最美丽,最复杂的方法,用于按顺序调用三个同步函数. (8认同)
  • 这不会延迟3,5和8秒的功能,如示例所示,它们将一个接一个地运行. (5认同)
  • 感谢您熟练解释异步和同步js函数之间的区别.这解释了很多. (4认同)
  • 这是*不*正确的,原因如下:(1) 3 次超时将在 10 秒后全部解决,因此所有 3 行同时触发。(2) 这种方法要求你提前知道持续时间和“安排”函数在未来发生,而不是等待链中较早的异步函数解决并触发。--- 相反,您希望通过回调、承诺或异步库使用以下答案之一。 (2认同)

Dom*_*see 32

这个答案使用promisesECMAScript 6标准的JavaScript功能.如果您的目标平台不支持promises,请使用PromiseJs对其进行填充.

在这里查看我的答案等到动画功能完成,直到运行另一个功能,如果你想使用jQuery动画.

这里是你的代码将会是什么样ES6 PromisesjQuery animations.

Promise.resolve($('#art1').animate({ 'width': '1000px' }, 1000).promise()).then(function(){
    return Promise.resolve($('#art2').animate({ 'width': '1000px' }, 1000).promise());
}).then(function(){
    return Promise.resolve($('#art3').animate({ 'width': '1000px' }, 1000).promise());
});
Run Code Online (Sandbox Code Playgroud)

普通方法也可以包含在内Promises.

new Promise(function(fulfill, reject){
    //do something for 5 seconds
    fulfill(result);
}).then(function(result){
    return new Promise(function(fulfill, reject){
        //do something for 5 seconds
        fulfill(result);
    });
}).then(function(result){
    return new Promise(function(fulfill, reject){
        //do something for 8 seconds
        fulfill(result);
    });
}).then(function(result){
    //do something with the result
});
Run Code Online (Sandbox Code Playgroud)

完成后then立即执行该方法Promise.通常,function传递给的返回值then作为结果传递给下一个.

但是如果Promise返回a,则下一个then函数将等待直到Promise执行完并收到它的结果(传递给的值fulfill).


Way*_*ett 20

听起来你并没有完全理解同步异步函数执行之间的区别.

您在更新中提供的代码会立即执行每个回调函数,这些函数会立即启动动画.但是,动画是异步执行的.它的工作原理如下:

  1. 在动画中执行一个步骤
  2. setTimeout使用包含下一个动画步骤和延迟的函数调用
  3. 一段时间过去了
  4. setTimeout执行的回调
  5. 回到第1步

这将继续,直到动画的最后一步完成.与此同时,您的同步功能早已完成.换句话说,你的通话animate功能并没有真正需要3秒钟.通过延迟和回调模拟效果.

你需要的是一个队列.在内部,jQuery的排队动画,只执行一旦其对应的动画完成回调.如果你的回调然后开始另一个动画,效果是它们按顺序执行.

在最简单的情况下,这相当于以下内容:

window.setTimeout(function() {
    alert("!");
    // set another timeout once the first completes
    window.setTimeout(function() {
        alert("!!");
    }, 1000);
}, 3000); // longer, but first
Run Code Online (Sandbox Code Playgroud)

这是一个通用的异步循环函数.它将按顺序调用给定的函数,等待每个函数之间指定的秒数.

function loop() {
    var args = arguments;
    if (args.length <= 0)
        return;
    (function chain(i) {
        if (i >= args.length || typeof args[i] !== 'function')
            return;
        window.setTimeout(function() {
            args[i]();
            chain(i + 1);
        }, 2000);
    })(0);
}    
Run Code Online (Sandbox Code Playgroud)

用法:

loop(
  function() { alert("sam"); }, 
  function() { alert("sue"); });
Run Code Online (Sandbox Code Playgroud)

显然,您可以修改此操作以获取可配置的等待时间,或立即执行第一个函数,或者在链falseapply的函数返回时或在指定上下文中的函数或您可能需要的任何其他函数时停止执行.


Ali*_*Ali 11

我相信异步库将为您提供一种非常优雅的方式来实现这一目标.虽然承诺和回调可能有点难以兼顾,但异步可以提供简洁的模式来简化您的思维过程.要以串行方式运行函数,您需要将它们放在异步瀑布中.在异步术语中,每个函数都被称为a task,它接受一些参数和a callback; 这是序列中的下一个功能.基本结构看起来像:

async.waterfall([
  // A list of functions
  function(callback){
      // Function no. 1 in sequence
      callback(null, arg);
  },
  function(arg, callback){
      // Function no. 2 in sequence
      callback(null);
  }
],    
function(err, results){
   // Optional final callback will get results for all prior functions
});
Run Code Online (Sandbox Code Playgroud)

我刚试着在这里简要解释一下这个结构.阅读瀑布指南以获取更多信息,它写得非常好.


use*_*905 8

你的函数应该采用一个回调函数,它在完成时被调用.

function fone(callback){
...do something...
callback.apply(this,[]);

}

function ftwo(callback){
...do something...
callback.apply(this,[]);
}
Run Code Online (Sandbox Code Playgroud)

那么用法就像:

fone(function(){
  ftwo(function(){
   ..ftwo done...
  })
});
Run Code Online (Sandbox Code Playgroud)