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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5187968/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 16:03:40  来源:igfitidea点击:

How should I call 3 functions in order to execute them one after the other?

javascriptasynchronouscallbackclosures

提问by texai

If I need call this functions one after other,

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

$('#art1').animate({'width':'1000px'},1000);        
$('#art2').animate({'width':'1000px'},1000);        
$('#art3').animate({'width':'1000px'},1000);        

I know in jQuery I could do something like:

我知道在 jQuery 中我可以执行以下操作:

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

But, let's assume that I'm not using jQuery and I want to call:

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

some_3secs_function(some_value);        
some_5secs_function(some_value);        
some_8secs_function(some_value);        

How I should call this functions in order to execute some_3secs_function, and AFTER that call ends, then execute some_5secs_functionand AFTER that call ends, then call some_8secs_function?

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

UPDATE:

更新:

This still not working:

这仍然不起作用:

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

Three animations start at same time

三个动画同时开始

Where is my mistake?

我的错误在哪里?

回答by Peter Olson

In Javascript, there are synchronousand asynchronousfunctions.

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

Synchronous Functions

同步功能

Most functions in Javascript are synchronous. If you were to call several synchronous functions in a row

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

doSomething();
doSomethingElse();
doSomethingUsefulThisTime();

they will execute in order. doSomethingElsewill not start until doSomethinghas completed. doSomethingUsefulThisTime, in turn, will not start until doSomethingElsehas completed.

他们将按顺序执行。在完成doSomethingElse之前不会开始doSomethingdoSomethingUsefulThisTime,反过来,doSomethingElse在完成之前不会启动。

Asynchronous Functions

异步函数

Asynchronous function, however, will not wait for each other. Let us look at the same code sample we had above, this time assuming that the functions are asynchronous

然而,异步函数不会互相等待。让我们看看上面的相同代码示例,这次假设函数是异步的

doSomething();
doSomethingElse();
doSomethingUsefulThisTime();

The functions will be initialized in order, but they will all execute roughly at the same time. You can't consistently predict which one will finish first: the one that happens to take the shortest amount of time to execute will finish first.

函数将按顺序初始化,但它们将大致同时执行。您无法始终如一地预测哪个将首先完成:恰好花费最短时间执行的那个将首先完成。

But sometimes, you want functions that are asynchronous to execute in order, and sometimes you want functions that are synchronous to execute asynchronously. Fortunately, this is possible with callbacks and timeouts, respectively.

但有时,您希望异步函数按顺序执行,有时您希望同步函数异步执行。幸运的是,这可以分别通过回调和超时实现。

Callbacks

回调

Let's assume that we have three asynchronous functions that we want to execute in order, some_3secs_function, some_5secs_function, and some_8secs_function.

假设我们有三个要按顺序执行的异步函数some_3secs_functionsome_5secs_function, 和some_8secs_function

Since functions can be passed as arguments in Javascript, you can pass a function as a callback to execute after the function has completed.

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

If we create the functions like this

如果我们创建这样的函数

function some_3secs_function(value, callback){
  //do stuff
  callback();
}

then you can call then in order, like this:

然后你可以按顺序调用 then ,如下所示:

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.
    });
  });
});

Timeouts

超时

In Javascript, you can tell a function to execute after a certain timeout (in milliseconds). This can, in effect, make synchronous functions behave asynchronously.

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

If we have three synchronous functions, we can execute them asynchronously using the setTimeoutfunction.

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

setTimeout(doSomething, 10);
setTimeout(doSomethingElse, 10);
setTimeout(doSomethingUsefulThisTime, 10);

This is, however, a bit ugly and violates the DRY principle[wikipedia]. We could clean this up a bit by creating a function that accepts an array of functions and a timeout.

然而,这有点难看,并且违反了DRY 原则[wikipedia]。我们可以通过创建一个接受函数数组和超时的函数来稍微清理一下。

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

This can be called like so:

这可以这样调用:

executeAsynchronously(
    [doSomething, doSomethingElse, doSomethingUsefulThisTime], 10);

In summary, if you have asynchronous functions that you want to execute syncronously, use callbacks, and if you have synchronous functions that you want to execute asynchronously, use timeouts.

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

回答by Domysee

This answer uses promises, a JavaScript feature of the ECMAScript 6standard. If your target platform does not support promises, polyfill it with PromiseJs.

这个答案使用标准promises的 JavaScript 特性ECMAScript 6。如果您的目标平台不支持promises,请使用PromiseJs对其进行polyfill

Look at my answer here Wait till a Function with animations is finished until running another Functionif you want to use jQueryanimations.

如果要使用动画,请在此处查看我的答案,请等待带有动画的函数完成,直到运行另一个函数jQuery

Here is what your code would look like with ES6 Promisesand jQuery animations.

这是您的代码使用ES6 Promisesand 的样子jQuery 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());
});

Normal methods can also be wrapped in Promises.

普通方法也可以包含在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
});

The thenmethod is executed as soon as the Promisefinished. Normally, the return value of the functionpassed to thenis passed to the next one as result.

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

But if a Promiseis returned, the next thenfunction waits until the Promisefinished executing and receives the results of it (the value that is passed to fulfill).

但是如果 aPromise被返回,下一个then函数会一直等到Promise执行完成并接收它的结果(传递给 的值fulfill)。

回答by Wayne

It sounds like you're not fully appreciating the difference between synchronousand asynchronousfunction execution.

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

The code you provided in your update immediatelyexecutes each of your callback functions, which in turn immediately start an animation. The animations, however, execute asyncronously. It works like this:

您在更新中提供的代码会立即执行您的每个回调函数,然后立即启动动画。然而,动画是异步执行的。它是这样工作的:

  1. Perform a step in the animation
  2. Call setTimeoutwith a function containing the next animation step and a delay
  3. Some time passes
  4. The callback given to setTimeoutexecutes
  5. Go back to step 1
  1. 在动画中执行一个步骤
  2. setTimeout使用包含下一个动画步骤和延迟的函数调用
  3. 过了一段时间
  4. setTimeout执行的回调
  5. 返回步骤 1

This continues until the last step in the animation completes. In the meantime, your synchronous functions have long ago completed. In other words, your call to the animatefunction doesn't reallytake 3 seconds. The effect is simulated with delays and callbacks.

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

What you need is a queue. Internally, jQuery queues the animations, only executing yourcallback once its corresponding animation completes. If your callback then starts another animation, the effect is that they are executed in sequence.

你需要的是一个队列。在内部,jQuery 对动画进行排队,只有其相应的动画完成后才执行您的回调。如果您的回调然后启动另一个动画,效果是它们按顺序执行。

In the simplest case this is equivalent to the following:

在最简单的情况下,这等效于以下内容:

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

Here's a general asynchronous looping function. It will call the given functions in order, waiting for the specified number of seconds between each.

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

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);
}    

Usage:

用法:

loop(
  function() { alert("sam"); }, 
  function() { alert("sue"); });

You could obviously modify this to take configurable wait times or to immediately execute the first function or to stop executing when a function in the chain returns falseor to applythe functions in a specified context or whatever else you might need.

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

回答by Ali

I believe the asynclibrary will provide you a very elegant way to do this. While promises and callbacks can get a little hard to juggle with, async can give neat patterns to streamline your thought process. To run functions in serial, you would need to put them in an async waterfall. In async lingo, every function is called a taskthat takes some arguments and a callback; which is the next function in the sequence. The basic structure would look something like:

我相信异步库将为您提供一种非常优雅的方式来做到这一点。虽然承诺和回调可能有点难以处理,但异步可以提供简洁的模式来简化您的思考过程。要串行运行函数,您需要将它们放在异步瀑布中。在异步术语中,每个函数都被称为 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
});

I've just tried to briefly explain the structure here. Read through the waterfall guidefor more information, it's pretty well written.

我只是试图在这里简要解释一下结构。通读瀑布指南以获取更多信息,它写得很好。

回答by user406905

your functions should take a callback function, that gets called when it finishes.

你的函数应该接受一个回调函数,当它完成时被调用。

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

}

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

then usage would be like:

那么用法如下:

fone(function(){
  ftwo(function(){
   ..ftwo done...
  })
});

回答by Trass Vasston

asec=1000; 

setTimeout('some_3secs_function("somevalue")',asec*3);
setTimeout('some_5secs_function("somevalue")',asec*5);
setTimeout('some_8secs_function("somevalue")',asec*8);

I won't go into a deep discussion of setTimeout here, but:

我不会在这里深入讨论 setTimeout,但是:

  • in this case I've added the code to execute as a string. this is the simplest way to pass a var into your setTimeout-ed function, but purists will complain.
  • you can also pass a function name without quotes, but no variable can be passed.
  • your code does not wait for setTimeout to trigger.
  • This one can be hard to get your head around at first: because of the previous point, if you pass a variable from your calling function, that variable will not exist anymore by the time the timeout triggers - the calling function will have executed and it's vars gone.
  • I have been known to use anonymous functions to get around all this, but there could well be a better way,
  • 在这种情况下,我添加了作为字符串执行的代码。这是将 var 传递到 setTimeout-ed 函数的最简单方法,但纯粹主义者会抱怨。
  • 您也可以传递不带引号的函数名,但不能传递变量。
  • 您的代码不会等待 setTimeout 触发。
  • 一开始可能很难理解这一点:由于前一点,如果您从调用函数中传递一个变量,那么到超时触发时该变量将不再存在 - 调用函数将已执行并且它是瓦尔斯不见了。
  • 众所周知,我使用匿名函数来解决所有这些问题,但很可能有更好的方法,

回答by Sorean

Since you tagged it with javascript, I would go with a timer control since your function names are 3, 5, and 8 seconds. So start your timer, 3 seconds in, call the first, 5 seconds in call the second, 8 seconds in call the third, then when it's done, stop the timer.

由于您使用 javascript 对其进行了标记,因此我将使用计时器控件,因为您的函数名称为 3、5 和 8 秒。所以启动你的计时器,3 秒后,调用第一个,5 秒调用第二个,8 秒调用第三个,然后当它完成时,停止计时器。

Normally in Javascript what you have is correct for the functions are running one after another, but since it looks like you're trying to do timed animation, a timer would be your best bet.

通常在 Javascript 中,您所拥有的功能是正确的,因为这些功能正在一个接一个地运行,但是由于看起来您正在尝试进行定时动画,因此计时器将是您最好的选择。

回答by yuuya

//sample01
(function(_){_[0]()})([
 function(){$('#art1').animate({'width':'10px'},100,this[1].bind(this))},
 function(){$('#art2').animate({'width':'10px'},100,this[2].bind(this))},
 function(){$('#art3').animate({'width':'10px'},100)},
])

//sample02
(function(_){_.next=function(){_[++_.i].apply(_,arguments)},_[_.i=0]()})([
 function(){$('#art1').animate({'width':'10px'},100,this.next)},
 function(){$('#art2').animate({'width':'10px'},100,this.next)},
 function(){$('#art3').animate({'width':'10px'},100)},
]);

//sample03
(function(_){_.next=function(){return _[++_.i].bind(_)},_[_.i=0]()})([
 function(){$('#art1').animate({'width':'10px'},100,this.next())},
 function(){$('#art2').animate({'width':'10px'},100,this.next())},
 function(){$('#art3').animate({'width':'10px'},100)},
]);

回答by Cuzox

You could also use promises in this way:

你也可以这样使用 promise:

    some_3secs_function(this.some_value).then(function(){
       some_5secs_function(this.some_other_value).then(function(){
          some_8secs_function(this.some_other_other_value);
       });
    });

You would have to make some_valueglobal in order to access it from inside the .then

您必须使some_value全局才能从 .then 内部访问它

Alternatively, from the outer function you could return the value the inner function would use, like so:

或者,您可以从外部函数返回内部函数将使用的值,如下所示:

    one(some_value).then(function(return_of_one){
       two(return_of_one).then(function(return_of_two){
          three(return_of_two);
       });
    });

回答by Biepbot Von Stirling

I use a 'waitUntil' function based on javascript's setTimeout

我使用基于 javascript 的 setTimeout 的“waitUntil”函数

/*
    funcCond : function to call to check whether a condition is true
    readyAction : function to call when the condition was true
    checkInterval : interval to poll <optional>
    timeout : timeout until the setTimeout should stop polling (not 100% accurate. It was accurate enough for my code, but if you need exact milliseconds, please refrain from using Date <optional>
    timeoutfunc : function to call on timeout <optional>
*/
function waitUntil(funcCond, readyAction, checkInterval, timeout, timeoutfunc) {
    if (checkInterval == null) {
        checkInterval = 100; // checkinterval of 100ms by default
    }
    var start = +new Date(); // use the + to convert it to a number immediatly
    if (timeout == null) {
        timeout = Number.POSITIVE_INFINITY; // no timeout by default
    }
    var checkFunc = function() {
        var end = +new Date(); // rough timeout estimations by default

        if (end-start > timeout) {
            if (timeoutfunc){ // if timeout function was defined
                timeoutfunc(); // call timeout function
            }
        } else {
            if(funcCond()) { // if condition was met
                readyAction(); // perform ready action function
            } else {
                setTimeout(checkFunc, checkInterval); // else re-iterate
            }
        }
    };
    checkFunc(); // start check function initially
};

This would work perfectly if your functions set a certain condition to true, which you would be able to poll. Plus it comes with timeouts, which offers you alternatives in case your function failed to do something (even within time-range. Think about user feedback!)

如果您的函数将某个条件设置为 true(您可以轮询该条件),这将非常有效。此外,它还带有超时功能,如果您的功能无法执行某些操作(即使在时间范围内。考虑用户反馈!),它会为您提供替代方案。

eg

例如

doSomething();
waitUntil(function() { return doSomething_value===1;}, doSomethingElse);
waitUntil(function() { return doSomethingElse_value===1;}, doSomethingUseful);

Notes

Date causes rough timeout estimates. For greater precision, switch to functions such as console.time(). Do take note that Date offers greater cross-browser and legacy support. If you don't need exact millisecond measurements; don't bother, or, alternatively, wrap it, and offer console.time() when the browser supports it

笔记

日期导致粗略的超时估计。为了获得更高的精度,请切换到诸如 console.time() 之类的函数。请注意 Date 提供了更好的跨浏览器和遗留支持。如果您不需要精确的毫秒测量;不要打扰,或者,或者,包装它,并在浏览器支持时提供 console.time()