如果我需要一个接一个地调用此函数,
$('#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);
}));
三个动画同时开始
我的错误在哪里?
在Javascript中,有同步和异步功能。
同步功能
Javascript中的大多数功能都是同步的。如果要连续调用多个同步函数
doSomething();
doSomethingElse();
doSomethingUsefulThisTime();
他们将按顺序执行。在完成doSomethingElse
之前不会启动doSomething
。doSomethingUsefulThisTime
,直到doSomethingElse
完成才开始。
异步功能
但是,异步功能不会互相等待。让我们看一下与上面相同的代码示例,这次假设函数是异步的
doSomething();
doSomethingElse();
doSomethingUsefulThisTime();
这些函数将按顺序初始化,但是它们将大致同时执行。您无法始终如一地预测哪个将最先完成:恰好花费最短时间执行的那个将最先完成。
但有时,您希望异步执行的功能按顺序执行,有时,您希望同步执行的功能异步执行。幸运的是,这分别可以通过回调和超时实现。
回呼
让我们假设我们有三个异步函数,我们想以此来执行,some_3secs_function
,some_5secs_function
,和some_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原则[wikipedia]。我们可以通过创建一个接受函数数组和超时的函数来对此进行清理。
function executeAsynchronously(functions, timeout) {
for(var i = 0; i < functions.length; i++) {
setTimeout(functions[i], timeout);
}
}
可以这样称呼:
executeAsynchronously(
[doSomething, doSomethingElse, doSomethingUsefulThisTime], 10);
总而言之,如果您具有要同步执行的异步功能,请使用回调,如果您要同步执行的同步功能,请使用超时。
该答案使用promises
,是ECMAScript 6
标准的JavaScript功能。如果目标平台不支持promises
,请用PromiseJs填充它。
在这里查看我的答案。如果要使用动画,请等到带有动画的功能完成,再运行另一个功能jQuery
。
这是您使用ES6 Promises
and编写的代码的样子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
});
该then
方法在Promise
完成后立即执行。通常,function
传递给的返回值then
作为结果传递给下一个。
但是,如果Promise
返回a,则下一个then
函数将等待直到Promise
完成执行并接收其结果(传递给的值fulfill
)。
听起来您似乎没有完全理解同步和异步函数执行之间的区别。
您在更新中提供的代码将立即执行您的每个回调函数,这些回调函数将立即启动动画。但是,动画是异步执行的。它是这样的:
- 在动画中执行一个步骤
setTimeout
使用包含下一个动画步骤和延迟的函数进行调用- 一段时间过去了
- 给予
setTimeout
执行的回调 - 返回步骤1
这一直持续到动画的最后一步完成为止。同时,您的同步功能早已完成。换句话说,你的通话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
在指定上下文中返回该功能时停止执行,或者可能需要执行其他任何操作。
我相信异步库将为您提供一种非常优雅的方法。虽然Promise和回调可能很难处理,但是异步可以提供简洁的模式来简化您的思考过程。要串行运行功能,您需要将它们放入异步瀑布中。在异步语言中,每个函数都称为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
});
我只是试图在这里简要解释其结构。通读瀑布指南以获取更多信息,它写得很好。
您的函数应采用回调函数,该函数在完成时会被调用。
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,但是:
- 在这种情况下,我添加了代码以字符串形式执行。这是将var传递到setTimeout-ed函数中的最简单方法,但是纯粹主义者会抱怨。
- 您还可以传递不带引号的函数名称,但不能传递变量。
- 您的代码不等待setTimeout触发。
- 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,
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中,您所拥有的功能是正确的,这些功能是一个接一个地运行的,但是由于您似乎想尝试制作定时动画,因此最好使用计时器。
//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)},
]);
您还可以通过以下方式使用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
为了从.then内部访问它,您必须将其设置为global。
或者,可以从外部函数返回内部函数将使用的值,如下所示:
one(some_value).then(function(return_of_one){
two(return_of_one).then(function(return_of_two){
three(return_of_two);
});
});
ES6更新
由于async / await现在已经广泛可用,因此可以通过以下方式实现这一目的:
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()
}
基本上是“使”函数(如果它们还不是异步的)“许诺”,然后等待它们
我使用基于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
};
如果您的函数将某个条件设置为true(可以轮询),则此方法将非常有效。此外,它还带有超时功能,可以在功能无法执行某些操作时(甚至在时间范围内,为您提供替代方案。请考虑用户反馈!)
例如
doSomething();
waitUntil(function() { return doSomething_value===1;}, doSomethingElse);
waitUntil(function() { return doSomethingElse_value===1;}, doSomethingUseful);
笔记
日期会导致粗略的超时估算。为了获得更高的精度,请切换到console.time()之类的函数。请注意,Date提供了更好的跨浏览器和旧版支持。如果您不需要精确的毫秒测量;不要打扰,或者换句话说,将其包装起来,并在浏览器支持时提供console.time()
如果必须在方法2、3、4之后执行方法1,则以下代码段可以是使用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>
文章标签:asynchronous , callback , closures , javascript
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!
评论已关闭!