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

2020/11/07 11:23 · javascript ·  · 0评论

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

$('#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之前不会启动doSomethingdoSomethingUsefulThisTime,直到doSomethingElse完成才开始

异步功能

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

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

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

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

回呼

让我们假设我们有三个异步函数,我们想以此来执行,some_3secs_functionsome_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 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());
});

普通方法也可以使用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)。

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

您在更新中提供的代码将立即执行您的每个回调函数,这些回调函数将立即启动动画。但是,动画是异步执行的它是这样的:

  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

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

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

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

我相信异步库将为您提供一种非常优雅的方法。虽然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>
本文地址:http://javascript.askforanswer.com/woyinggairuhediaoyong3gehanshucainengyigejieyigedizhixingtamen.html
文章标签: ,   ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!