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

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

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

我知道在 jQuery 中我可以这样做:

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

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

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

为了执行 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);
}));

三个动画同时开始

我的错误在哪里?

337844 次浏览

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

通常在 Javascript 中,由于函数一个接一个地运行,所以你得到的结果是正确的,但是因为看起来你想要做定时动画,所以定时器将是你最好的选择。

在 Javascript 中,有 同步异步的函数。

同步功能

Javascript 中的大多数函数是同步的

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

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

异步函数

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

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

这些函数将按顺序初始化,但它们将大致同时执行。你无法一致地预测哪一个会第一个完成: 碰巧用最短时间执行的那个会第一个完成。

但是有时候,您希望异步函数按顺序执行,有时候您希望同步函数异步执行。幸运的是,分别使用回调和超时可以做到这一点。

复试

假设我们有三个希望按顺序执行的异步函数: some_3secs_functionsome_5secs_functionsome_8secs_function

因为函数可以在 Javascript 中作为参数传递,所以可以将函数作为回调传递,以便在函数完成后执行。

如果我们像这样创建函数

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

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

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

暂停

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

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

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

然而,这有点丑陋,并且违反了 DRY 原则 < sup > [维基百科] 。我们可以通过创建一个接受函数数组和超时的函数来简化这个问题。

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

可以这样说:

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

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

你的函数应该有一个回调函数,当它结束时被调用。

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


}


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

那么用法就是:

fone(function(){
ftwo(function(){
..ftwo done...
})
});
asec=1000;


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

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

  • 在本例中,我添加了作为字符串执行的代码。这是向 setTimeout 函数传递 var 的最简单方法,但是纯粹主义者会抱怨。
  • 也可以传递不带引号的函数名,但不能传递变量。
  • 您的代码不会等待 setTimeout 触发。
  • 一开始你可能很难理解这一点: 因为之前的观点,如果你从调用函数中传递一个变量,那么当超时触发时,该变量将不再存在——调用函数将已经执行,它的 vars 消失了。
  • 众所周知,我使用匿名函数来解决这些问题,但是很可能有更好的方法,

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

在更新 马上中提供的代码执行每个回调函数,这些函数立即启动一个动画。然而,动画执行 不同步地。工作原理是这样的:

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

这将一直持续到动画中的最后一步完成。与此同时,您的同步功能早已完成。换句话说,对 animate函数的调用不需要3秒钟。这种效果是通过延迟和回调来模拟的。

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

在最简单的情况下,这相当于下列情况:

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

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

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

用法:

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

很明显,您可以修改它以获取可配置的等待时间,或者立即执行第一个函数,或者当链中的一个函数返回 false时停止执行,或者在指定的上下文中返回 apply的函数,或者您可能需要的其他任何东西。

这个答案使用 promises,这是 ECMAScript 6标准的一个 JavaScript 特性。如果您的目标平台不支持 promises,请用 承诺填充它。

如果你想使用 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());
});

常规方法也可以包装在 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
});

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

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

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

我只是想简单解释一下这里的结构。阅读瀑布 向导以获得更多信息,它写得非常好。

你也可以这样使用承诺:

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

您必须使 some_value全局化,以便从. then 内部访问它

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

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

ES6更新

由于现在可以广泛使用异步/等待,因此可以采用下面的方法来实现同样的功能:

async function run(){
await $('#art1').animate({'width':'1000px'},1000,'linear').promise()
await $('#art2').animate({'width':'1000px'},1000,'linear').promise()
await $('#art3').animate({'width':'1000px'},1000,'linear').promise()
}

基本上就是“承诺”你的函数(如果它们还没有异步的话) ,然后等待它们

//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)},
]);

我使用了一个基于 javascript 的 setTimeout 的“ wait until”函数

/*
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
};

如果您的函数将某个条件设置为 true,您就可以进行轮询,那么这将非常完美。另外,它还带有超时,这为您提供了备选方案,以防您的功能无法执行某些操作(即使在时间范围内)。考虑用户反馈!)

例句

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

笔记

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

如果方法1必须在方法2,3,4之后执行。下面的代码片段可以用 JavaScript 中的 Deferred 对象解决这个问题。

function method1(){
var dfd = new $.Deferred();
setTimeout(function(){
console.log("Inside Method - 1");
method2(dfd);
}, 5000);
return dfd.promise();
}


function method2(dfd){
setTimeout(function(){
console.log("Inside Method - 2");
method3(dfd);
}, 3000);
}


function method3(dfd){
setTimeout(function(){
console.log("Inside Method - 3");
dfd.resolve();
}, 3000);
}


function method4(){
console.log("Inside Method - 4");
}


var call = method1();


$.when(call).then(function(cb){
method4();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>