我应该怎样调用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
和AFTER那个调用结束,然后调用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); }));
三个animation同时开始
我的错误在哪里?
在Javascript中,有同步和asynchronousfunction。
同步function
Javascript中的大部分函数都是同步的。 如果你要连续调用几个同步函数
doSomething(); doSomethingElse(); doSomethingUsefulThisTime();
他们将按顺序执行。 doSomethingElse
在doSomething
完成之前不会启动。 doSomethingUsefulThisTime
,反过来,将不会开始,直到doSomethingElse
完成。
asynchronous函数
asynchronous函数,但是,不会等待对方。 让我们看看上面的代码示例,这次假设这些函数是asynchronous的
doSomething(); doSomethingElse(); doSomethingUsefulThisTime();
函数将按顺序初始化,但它们都将大致同时执行。 你不能一致地预测哪一个会先完成:恰好花费最短时间执行的那个会先完成。
但是有时候,你需要按顺序执行asynchronous的函数,有时你需要asynchronous执行的函数来asynchronous执行。 幸运的是,这可能分别与callback和超时。
callback
假设我们有三个some_3secs_function
顺序执行的asynchronous函数, some_3secs_function
, some_5secs_function
和some_8secs_function
。
由于函数可以在Javascript中作为parameter passing,因此可以在函数完成后将函数作为callback函数执行。
如果我们创build这样的function
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中,你可以告诉一个函数执行一定的超时(以毫秒为单位)。 实际上,这可以使同步函数asynchronous行为。
如果我们有三个同步函数,我们可以使用setTimeout
函数asynchronous执行它们。
setTimeout(doSomething, 10); setTimeout(doSomethingElse, 10); setTimeout(doSomethingUsefulThisTime, 10);
但是,这有点丑陋,违反了DRY原则[维基百科] 。 我们可以通过创build一个接受一系列函数和超时的函数来清理这个问题。
function executeAsynchronously(functions, timeout) { for(var i = 0; i < functions.length; i++) { setTimeout(functions[i], timeout); } }
这可以这样调用:
executeAsynchronously( [doSomething, doSomethingElse, doSomethingUsefulThisTime], 10);
总而言之,如果您有要同步执行的asynchronous函数,请使用callback函数,并且如果您有要asynchronous执行的同步函数,请使用超时。
这听起来像你不完全理解同步和asynchronous函数执行的区别。
您在更新中提供的代码会立即执行您的每个callback函数,然后立即启动一个animation。 然而,animation执行asynchronous 。 它是这样工作的:
- 在animation中执行一个步骤
- 用包含下一个animation步骤和延迟的函数调用
setTimeout
- 一段时间过去了
- 给
setTimeout
的callback执行 - 回到步骤1
这一直持续到animation的最后一步完成。 同时,你的同步function早已完成。 换句话说,你对animate
函数的调用并不需要3秒。 效果是模拟延迟和callback。
你需要的是一个队列 。 在内部,jQuery排队animation,只有在相应的animation完成后才执行callback。 如果您的callback开始另一个animation,效果是,他们顺序执行。
在最简单的情况下,这相当于以下内容:
window.setTimeout(function() { alert("!"); // set another timeout once the first completes window.setTimeout(function() { alert("!!"); }, 1000); }, 3000); // longer, but first
这是一个通用的asynchronous循环函数。 它将按顺序调用给定的函数,等待每个指定的秒数。
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"); });
你显然可以修改这个来进行可configuration的等待时间,或者立即执行第一个函数,或者当链中的函数返回false
或者在指定的上下文中apply
函数或者其他任何你可能需要的函数时停止执行。
这个答案使用了ECMAScript 6
标准的JavaScript特性promises
。 如果您的目标平台不支持promises
,请使用PromiseJs进行填充 。
看看我的答案在这里等到有animation的函数完成,直到运行另一个函数,如果你想使用jQuery
animation。
这里是你的代码看起来像ES6 Promises
和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()); });
普通方法也可以包装在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 });
Promise
完成后立即执行该方法。 正常情况下,传递给它的function
的返回值作为结果传递给下一个function
。
但是,如果Promise
被返回,那么下一个函数将等待Promise
完成执行并接收它的结果(传递的值)。
你的函数应该有一个callback函数,当它完成时被调用。
function fone(callback){ ...do something... callback.apply(this,[]); } function ftwo(callback){ ...do something... callback.apply(this,[]); }
那么用法就像
fone(function(){ ftwo(function(){ ..ftwo done... }) });
我相信asynchronous库将为您提供一个非常优雅的方式来做到这一点。 虽然承诺和callback可以有点难以兼顾,asynchronous可以给整洁的模式,以简化你的思维过程。 要以串行方式运行函数,您需要将它们放在asynchronous瀑布中 。 在asynchronous术语中,每个函数被称为一个task
,需要一些参数和一个callback
; 这是序列中的下一个function。 基本结构看起来像这样:
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 });
我只是试图在这里简要地解释一下这个结构。 通过瀑布指南了解更多信息,这是写得很好。
asec=1000; setTimeout('some_3secs_function("somevalue")',asec*3); setTimeout('some_5secs_function("somevalue")',asec*5); setTimeout('some_8secs_function("somevalue")',asec*8);
我不会在这里深入讨论setTimeout,但是:
- 在这种情况下,我已经添加了代码来执行一个string。 这是将var传递给setTimeout-ed函数的最简单的方法,但纯粹主义者会抱怨。
- 您也可以传递不带引号的函数名称,但不能传递variables。
- 你的代码不会等待setTimeout触发。
- 这个很难让你头脑发热:因为以前的观点,如果你从你的调用函数传递一个variables,这个variables在超时触发时就不会再存在了 – 调用函数将会执行,瓦尔斯消失了。
- 我已经知道使用匿名函数来解决所有这些,但是可能有更好的方法,
既然你用javascript标记了它,我会去用一个计时器控件,因为你的函数名是3,5和8秒。 所以启动你的计时器,3秒钟进入,第一次呼叫,第二秒钟5秒钟,第三秒钟呼叫8秒钟,然后停止计时器。
通常在Javascript中,你所拥有的function是一个接一个地运行是正确的,但是因为它看起来像你正在尝试做定时animation,所以定时器将是你最好的select。
你也可以用这种方式使用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); }); });
你将不得不使some_value
全球为了从内部访问它
或者,从外部函数你可以返回内部函数将使用的值,如下所示:
one(some_value).then(function(return_of_one){ two(return_of_one).then(function(return_of_two){ three(return_of_two); }); });
//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的'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 };
如果你的函数将某些条件设置为真,你就可以进行轮询。 另外它还有超时function,当你的function不能做某些事情(甚至在时间范围内)时,它提供了另外的select,想想用户的反馈!
例如
doSomething(); waitUntil(function() { return doSomething_value===1;}, doSomethingElse); waitUntil(function() { return doSomethingElse_value===1;}, doSomethingUseful);
笔记
date导致粗略的超时估计。 为了获得更高的精确度,请切换到诸如console.time()之类的函数。 请注意date提供了更大的跨浏览器和传统的支持。 如果你不需要精确的毫秒测量, 不要打扰,或者换一种方式来包装它,并在浏览器支持时提供console.time()