3 işlevi birbiri ardına yürütmek için nasıl çağırmalıyım?


151

Bu işlevleri birbiri ardına çağırmam gerekirse,

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

Ben jQuery gibi bir şey yapabilir biliyorum:

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

Ama diyelim ki jQuery kullanmıyorum ve şunu aramak istiyorum:

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

Çalıştırmak için bu işlevleri nasıl çağırmalıyım some_3secs_functionve bu çağrı bittikten SONRA, daha some_5secs_functionsonra bu çağrıyı yürütün ve SONRA, sonra çağırın some_8secs_function?

GÜNCELLEME:

Bu hala çalışmıyor:

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

Üç animasyon aynı anda başlar

Benim hatam nerede?


işlevlerin tam olarak 3 5 & 8 saniyede mi yoksa birbiri ardına mı çağrıldığını mı kastediyorsunuz?
Trass Vasston

Ben düşünüyorum Eşzamanlı vs asenkron fonksiyon icra konusunda basitçe emin değilseniz. Cevabımı aşağıda güncelledim. Umarım yardımcı olur.
Wayne

Yanıtlar:


243

Javascript'te senkron ve asenkron fonksiyonlar vardır.

Senkron Fonksiyonlar

Javascript'teki işlevlerin çoğu eşzamanlıdır. Arka arkaya birkaç eşzamanlı işlevi çağırırsanız

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

sırayla infaz edecekler. tamamlanıncaya doSomethingElsekadar başlamaz doSomething. doSomethingUsefulThisTime, sırayla, doSomethingElsetamamlanana kadar başlamaz .

Asenkron Fonksiyonlar

Ancak asenkron işlev birbirini beklemez. Bu sefer fonksiyonların eşzamansız olduğunu varsayarsak, yukarıdaki kod örneğine bakalım

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

İşlevler sırayla başlatılır, ancak hepsi kabaca aynı anda yürütülür. Hangisinin önce biteceğini sürekli olarak tahmin edemezsiniz: yürütmek için en kısa sürenin gerçekleşmesi önce biter.

Ancak bazen, eşzamansız olan işlevlerin sırayla yürütülmesini ve bazen eşzamanlı işlevlerin eşzamansız olarak yürütülmesini istersiniz. Neyse ki, bu sırasıyla geri aramalar ve zaman aşımları ile mümkündür.

Callbacks

Diyelim biz sırayla çalıştırmak istiyorum, o üç asenkron işlevlere sahip olduğunu varsayalım some_3secs_function, some_5secs_functionve some_8secs_function.

İşlevler Javascript'te bağımsız değişken olarak iletilebildiğinden, işlev tamamlandıktan sonra yürütmek için bir işlevi geri arama olarak iletebilirsiniz.

Eğer böyle fonksiyonlar yaratırsak

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

sonra sırayla şöyle çağırabilirsiniz:

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

Zaman aşımları

Javascript'te, bir işlevin belirli bir zaman aşımından sonra (milisaniye cinsinden) yürütülmesini söyleyebilirsiniz. Bu aslında senkron fonksiyonların asenkron olarak davranmasını sağlayabilir.

Üç senkron fonksiyonumuz varsa, setTimeoutfonksiyonu kullanarak bunları senkronize olmayan bir şekilde çalıştırabiliriz .

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

Ancak bu biraz çirkin ve KURU prensibini [wikipedia] ihlal ediyor . Bir dizi işlevi ve bir zaman aşımını kabul eden bir işlev oluşturarak bunu biraz temizleyebiliriz.

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

Bu şu şekilde adlandırılabilir:

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

Özet olarak, eşzamanlı olarak yürütmek istediğiniz eşzamansız işlevleriniz varsa geri çağrıları kullanın ve eşzamansız olarak yürütmek istediğiniz eşzamanlı işlevleriniz varsa zaman aşımlarını kullanın.


7
Bu, örnekte önerildiği gibi işlevleri 3,5 ve 8 saniye geciktirmez, sadece birbiri ardına çalışırlar.
Trass Vasston

1
@Peter - Bekle, kafam karıştı. Bunlar tamamlanması birkaç saniye süren basit eşzamanlı çağrılarsa, neden bunlardan birine ihtiyacımız var?
Wayne

9
@Peter - Üç eşzamanlı işlevi sırayla çağırmak için gördüğüm en güzel, kıvrık yöntem için +1.
Wayne

4
Async ve sync js işlevleri arasındaki farkı ustalıkla açıkladığınız için teşekkür ederiz. Bu çok açıklıyor.
jnelson

2
Bu, aşağıdaki nedenlerden dolayı doğru DEĞİLDİR : (1) 3 zaman aşımı süresi 10 saniye sonra çözülür, bu nedenle 3 satırın tümü aynı anda tetiklenir. (2) bu yöntem, zincirdeki önceki zaman uyumsuz işlevlerini beklemek ve tetikleyici olmak yerine, ilerideki süreyi ve "zamanlama" işlevlerini gelecekte gerçekleşmesini bilmenizi gerektirir. --- bunun yerine geri çağrıları, vaatleri veya zaman uyumsuz kitaplığı kullanarak aşağıdaki cevaplardan birini kullanmak istersiniz.
zeroasterisk

37

Bu cevap promises, ECMAScript 6standardın JavaScript özelliğini kullanır . Hedef platformunuz desteklemiyorsa PromiseJspromises ile çoklu doldurun .

Cevabımı buraya bakın Animasyonları kullanmak istiyorsanız, başka bir Fonksiyonu çalışana kadar animasyonlu bir Fonksiyonun bitmesini bekleyinjQuery .

Kodunuz ES6 Promisesve ile böyle görünecektir 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 yöntemler de sarılabilir 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
});

thenYöntem en kısa sürede yürütülür Promisebitmiş. Normalde, functioniletilen dönüş değeri thensonuç olarak bir sonrakine geçirilir.

Ancak a Promisedöndürülürse, sonraki thenişlev Promisebitmiş yürütme işlemi bitene kadar bekler ve bunun sonuçlarını alır (iletilen değer fulfill).


Bu yararlı olduğunu biliyorum, ama bazı bağlam vermek için gerçek bir dünya örneği aramadan anlamak zor verilen kodu bulundu. Bu videoyu YouTube'da buldum: youtube.com/watch?v=y5mltEaQxa0 - ve videoyu buradan videoya yazdım drive.google.com/file/d/1NrsAYs1oaxXw0kv9hz7a6LjtOEb6x7z-/… Burada eksik olan kayıp gibi bazı nüanslar var bunun üzerinde durduğu örnek. (getPostById () satırında farklı bir kimlik kullanın veya bir yazının adıyla eşleşmemesi için bir yazarın adını değiştirmeyi deneyin)
JGFMK

20

Senkron ve asenkron fonksiyon yürütme arasındaki farkı tam olarak takdir etmediğiniz anlaşılıyor.

Güncellemenizde sağladığınız kod , geri arama işlevlerinizin her birini hemen yürütür ve bu da hemen bir animasyon başlatır. Ancak animasyonlar eşzamansız olarak yürütülür . Şöyle çalışır:

  1. Animasyonda bir adım gerçekleştirin
  2. setTimeoutSonraki animasyon adımını ve gecikmeyi içeren bir işlevle çağrı
  3. Biraz zaman geçti
  4. Verilen geri arama setTimeoutçalıştırır
  5. 1. adıma geri dönün

Bu, animasyondaki son adım tamamlanana kadar devam eder. Bu arada, senkron işlevleriniz uzun zaman önce tamamlanmıştır. Başka bir deyişle, animateişleve yapılan aramanız gerçekten 3 saniye sürmez. Efekt, gecikmeler ve geri aramalarla simüle edilir.

İhtiyacınız olan şey bir kuyruk . Dahili olarak, jQuery sadece yürütme, animasyonlar kuyruklar senin karşılık gelen animasyon tamamlanıncaya kez geri arama. Geri aramanız başka bir animasyon başlatırsa, sonuç bunların sırayla yürütülmesidir.

En basit durumda, bu aşağıdakilere eşdeğerdir:

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

İşte genel bir eşzamansız döngü işlevi. Verilen işlevleri sırayla çağıracak ve her biri arasında belirtilen saniye sayısını bekleyecektir.

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

Kullanımı:

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

Bunu, yapılandırılabilir bekleme süreleri almak veya ilk işlevi hemen yürütmek ya da zincirdeki bir işlev döndüğünde falseya da applybelirli bir bağlamdaki işlevlere ya da başka bir şeye ihtiyacınız olduğunda yürütmeyi durdurmak için değiştirebilirsiniz .


14

Async kütüphanesinin size bunu yapmanın çok zarif bir yolunu sağlayacağına inanıyorum . Vaatler ve geri çağrılarla uğraşmak biraz zorlaşırken, async düşünce sürecinizi kolaylaştırmak için düzgün desenler verebilir. İşlevleri seri olarak çalıştırmak için, bunları zaman uyumsuz bir şelaleye koymanız gerekir . Zaman uyumsuz dilde, her işleve taskbazı argümanlar ve a callback; dizideki bir sonraki işlevdir. Temel yapı şöyle görünecektir:

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

Buradaki yapıyı kısaca açıklamaya çalıştım. Daha fazla bilgi için şelale kılavuzunu okuyun , oldukça iyi yazılmış.


1
Bu gerçekten JS'yi biraz daha katlanılabilir kılıyor.
mike

9

işlevleriniz bittiğinde çağrılan bir geri çağırma işlevi almalıdır.

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

}

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

o zaman kullanım şöyle olurdu:

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

4
asec=1000; 

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

Burada setTimeout ile ilgili derin bir tartışmaya girmeyeceğim, ama:

  • bu durumda bir dize olarak yürütmek için kod ekledim. setTimeout-ed işlevinize bir var değerini iletmenin en basit yolu budur, ancak puristler şikayet edecektir.
  • bir işlev adını tırnak işaretleri olmadan da iletebilirsiniz, ancak hiçbir değişken iletilemez.
  • kodunuz setTimeout'un tetiklenmesini beklemez.
  • Bu ilk başta başınızı döndürmek zor olabilir: önceki nokta nedeniyle, arama işlevinizden bir değişken iletirseniz, zaman aşımı tetiklendiğinde bu değişken artık mevcut olmaz - arama işlevi yürütülür ve vars gitti.
  • Tüm bunları aşmak için anonim işlevler kullandığım biliniyordu, ancak daha iyi bir yol olabilirdi,

3

Javascript ile etiketlediğiniz için, işlev adlarınız 3, 5 ve 8 saniye olduğundan bir zamanlayıcı kontrolü ile giderdim. Bu yüzden zamanlayıcınızı başlatın, 3 saniye içinde ilk, 5 saniye içinde ikinci, 5 saniye içinde üçüncü, 8 saniye sonra tamamlayın, zamanlayıcıyı durdurun.

Normalde Javascript'te, fonksiyonların birbiri ardına çalışması için doğru olan, ancak zamanlanmış animasyon yapmaya çalıştığınız gibi göründüğü için, bir zamanlayıcı en iyi bahistir.


2

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


Bunun ne olduğunu açıklayabilir misiniz? Alt çizgiye ne bağlı? Atanan işlev ne nextişe yarar?
mtso

1
Örnek 2'yi jsfiddle kullanarak açıklarım. jsfiddle.net/mzsteyuy/3 Kabaca açıklamama izin verirseniz, örnek 2 jsfiddle'daki kodun kısa bir yoludur. alt çizgi, öğelerin karşı değerli (i) ve sonraki işlev ve işlev [0] ~ [2] olan Dizi'dir.
yuuya

1

Vaatleri şu şekilde de kullanabilirsiniz:

    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 içinden erişebilmek için küresel olmalısın

Alternatif olarak, dış fonksiyondan iç fonksiyonun kullanacağı değeri döndürebilirsiniz, şöyle:

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

0

Javascript setTimeout dayalı bir 'waitUntil' işlevi kullanın

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

Eğer işlevleriniz yoklamayı başarabileceğiniz belirli bir koşulu doğru olarak ayarlarsa bu mükemmel bir şekilde çalışır. Ayrıca, fonksiyonunuzun bir şey yapamaması durumunda size alternatifler sunan zaman aşımları ile birlikte gelir (zaman aralığında bile. Kullanıcı geri bildirimlerini düşünün!)

Örneğin

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

notlar

Tarih, zor zaman aşımı tahminlerine neden olur. Daha fazla hassasiyet için console.time () gibi işlevlere geçin. Date'in daha fazla çapraz tarayıcı ve eski destek sunduğunu unutmayın. Tam milisaniye ölçümlerine ihtiyacınız yoksa; rahatsız etmeyin veya alternatif olarak sarın ve tarayıcı desteklediğinde console.time ()


0

Yöntem 1, yöntem 2, 3, 4'ten sonra yürütülmesi gerekiyorsa, aşağıdaki kod snippet'i, JavaScript'te Ertelenmiş nesne kullanılarak bunun çözümü olabilir.

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>

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.