Devam etmeden önce bir işlevin bitmesini beklemenin doğru yolu?


187

İki JS fonksiyonum var. Biri diğerini arar. Arama işlevi içinde, diğerini aramak, bu işlevin bitmesini beklemek, sonra devam etmek istiyorum. Yani, örneğin / sözde kod:

function firstFunction(){
    for(i=0;i<x;i++){
        // do something
    }
};

function secondFunction(){
    firstFunction()
    // now wait for firstFunction to finish...
    // do something else
};

Bu çözümü buldum, ancak bunun akıllıca bir yol olup olmadığını bilmiyorum.

var isPaused = false;

function firstFunction(){
    isPaused = true;
    for(i=0;i<x;i++){
        // do something
    }
    isPaused = false;
};

function secondFunction(){
    firstFunction()
    function waitForIt(){
        if (isPaused) {
            setTimeout(function(){waitForIt()},100);
        } else {
            // go do that thing
        };
    }
};

Bu yasal mı? Bununla baş etmenin daha zarif bir yolu var mı? Belki jQuery ile?


11
Bu firstFunctiontam olarak ne zaman uyumsuz hale getirir? Her iki şekilde de - sözleri kontrol et
zerkms

İlk fonksiyon, saniyenin her 10 saniyesinde bir skor saatini hızla güncelliyor. Hangi ... düşünmek için gel, sanırım biz önceden hesaplamak ve sonra el ile setTimeout üzerinden ikinci işlev çağrısı duraklatmak olabilir. Bununla birlikte, başka bir yerde bir duraklama yeteneğine sahip olma arzusunu görebiliyorum.
DA.

bir duraklamaya ihtiyacınız yok - jquery vaatleri için google
zerkms

@zerkms ilginç görünüyor vaat ediyor! Hala tarayıcı desteği araştırılıyor ...
DA.

1
Aynı problemim var.
Vappor Washmade

Yanıtlar:


142

Bunun gibi eşzamansız çalışmalarla başa çıkmanın bir yolu, bir geri arama işlevi kullanmaktır, örneğin:

function firstFunction(_callback){
    // do some asynchronous work
    // and when the asynchronous stuff is complete
    _callback();    
}

function secondFunction(){
    // call first function and pass in a callback function which
    // first function runs when it has completed
    firstFunction(function() {
        console.log('huzzah, I\'m done!');
    });    
}

@Janaka Pushpakumara'nın önerisine göre, şimdi aynı şeyi elde etmek için ok işlevlerini kullanabilirsiniz. Örneğin:

firstFunction(() => console.log('huzzah, I\'m done!'))


Güncelleme: Bunu bir süre önce cevapladım ve gerçekten güncellemek istiyorum. Geri aramalar kesinlikle iyi olsa da, benim deneyimime göre, okunması ve bakımı daha zor olan kodlarla sonuçlanma eğilimindedir. Yine de bunları hala kullandığım durumlar var, örneğin ilerleme olaylarını ve benzerlerini parametre olarak geçirmek. Bu güncelleme sadece alternatifleri vurgulamak içindir.

Ayrıca orijinal soru specificallty sizin işlev senkron ise, o kadar halinde herkes kafası karışık, asenk söz etmez edecek aradığında bloke ederler. Örneğin:

doSomething()
// the function below will wait until doSomething completes if it is synchronous
doSomethingElse()

Zaten ima edildiği gibi, işlev asenkron ise, bugün tüm asenkron işlerimle başa çıkma eğilimim asenkron / beklemedir. Örneğin:

const secondFunction = async () => {
  const result = await firstFunction()
  // do something else here after firstFunction completes
}

IMO, async / await, kodunuzu doğrudan vaat kullanmaktan (çoğu zaman) çok daha okunabilir hale getirir. Yakalama hatalarını işlemeniz gerekiyorsa, bunu try / catch ile kullanın. Bununla ilgili daha fazla bilgiyi buradan edinebilirsiniz: https://developer.mozilla.org/tr-TR/docs/Web/JavaScript/Reference/Statements/async_function .


9
@zerkms - Ayrıntılara dikkat etmek ister misiniz?
Matt Way

7
geri çağrılar asenkron ile başa çıkmak için elverişsiz, vaatler çok daha kolay ve esnek
zerkms

1
En iyi (güncellenmiş) kelimesini kullanmamam gerektiği konusunda haklı olsanız da, geri aramaların ve vaatlerin rahatlığı sorunun karmaşıklığına bağlıdır.
Matt Way

3
Bu offtopic olur, ama şahsen ben bugünlerde geri arama tercih etmek için bir neden görmüyorum :-) Son 2 yıl içinde yazılan kodlarımdan herhangi bir vaat üzerinde geri arama sağlanan hatırlayamıyorum.
zerkms

3
İsterseniz es6 secondFunction () {firstFunction ((response) => {console.log (response);}); }
Janaka Pushpakumara

53

Zaman uyumsuz / beklenti kullanın:

async function firstFunction(){
  for(i=0;i<x;i++){
    // do something
  }
  return;
};

geri dönmesini beklemek için diğer işlevinizde bekleyin:

async function secondFunction(){
  await firstFunction();
  // now wait for firstFunction to finish...
  // do something else
};

9
eski tarayıcıları desteklediğimizde IE, async / await'i desteklemiyor
kyle

Bu, her iki fonksiyonun dışında da kullanılabilir $(function() { await firstFunction(); secondFunction(); });mi?
Lewistrick

1
@Lewistrick Unutmayın awaitsadece bir asyncyöntem içinde kullanılabilir . Böylece ebeveyn işlevinizi asyncyaparsanız, istediğiniz async methodsveya bu olmadan awaitistediğiniz kadarını arayabilirsiniz .
Fawaz

O mümkün mü awaitbir setTimeout?
Shayan

1
@Shayan evet, setTimeout'u zaman aşımından sonra bir sözü çözen başka bir işlevde sarın.
Fawaz

51

Burada önemli bir noktayı kaçırdığınız görülüyor: JavaScript tek iş parçacıklı bir yürütme ortamıdır. Kodunuza tekrar bakalım, ekledim alert("Here"):

var isPaused = false;

function firstFunction(){
    isPaused = true;
    for(i=0;i<x;i++){
        // do something
    }
    isPaused = false;
};

function secondFunction(){
    firstFunction()

    alert("Here");

    function waitForIt(){
        if (isPaused) {
            setTimeout(function(){waitForIt()},100);
        } else {
            // go do that thing
        };
    }
};

Beklemek zorunda değilsiniz isPaused. Eğer "İşte" uyarısı gördüğünüzde, isPausedolacak falsezaten ve firstFunctiongetiri sağlamış olur. Bunun nedeni, fordöngü ( // do something) içinden "veremeyiz" , döngü kesilmeyebilir ve önce tam olarak tamamlanması gerekir (daha fazla ayrıntı: Javascript iş parçacığı işleme ve yarış koşulları ).

Bununla birlikte, kod akışını firstFunctioneşzamansız hale getirebilir ve arayanı bilgilendirmek için geri arama veya söz verebilirsiniz. Sen üzerine vazgeçmek zorunda kalacak fordöngü ve onu taklit ifyerine ( JSFiddle ):

function firstFunction()
{
    var deferred = $.Deferred();

    var i = 0;
    var nextStep = function() {
        if (i<10) {
            // Do something
            printOutput("Step: " + i);
            i++;
            setTimeout(nextStep, 500); 
        }
        else {
            deferred.resolve(i);
        }
    }
    nextStep();
    return deferred.promise();
}

function secondFunction()
{
    var promise = firstFunction();
    promise.then(function(result) { 
        printOutput("Result: " + result);
    });
}

Bir yan notta, JavaScript 1.7, yieldanahtar kelimeleri jeneratörlerin bir parçası olarak tanıttı . Bu, aksi takdirde senkronize JavaScript kodu akışında eşzamansız delikleri "delmeye" izin verir ( daha fazla ayrıntı ve bir örnek ). Ancak, jeneratörler için tarayıcı desteği şu anda Firefox ve Chrome, AFAIK ile sınırlıdır.


3
Beni kurtardın. $ .Deferred () benim için vurulduğum şeydir. Teşekkürler
Temitayo

@noseratio Bunu denedim. Ancak waitForIt yöntemi hiç çağrılmaz. Neyi yanlış yapıyorum?
vigamage

@vigamage, denediklerinizin jsfiddle veya kod satırına bir bağlantı sağlayabilir misiniz?
noseratio

1
Merhaba, İlginiz için teşekkür ederim. Çalıştırdım. Teşekkür ederim..!
vigamage

18

İlk tamamlamak için bir işlev kullanmaktır için şık bir yolu beklemek Promises ile zaman uyumsuz / bekliyoruz fonksiyonu.


  1. İlk olarak, bir söz oluşturun . Oluşturduğum fonksiyon 2 saniye sonra tamamlanacak. setTimeoutTalimatların yürütülmesinin biraz zaman alacağı durumu göstermek için kullandım .
  2. İkinci işlev için, talimatlara devam etmeden önce ilk işlevin tamamlanacağı asenkron / await işlevini kullanabilirsiniz await.

Misal:

    //1. Create a new function that returns a promise
    function firstFunction() {
      return new Promise((resolve, reject) => {
          let y = 0
          setTimeout(() => {
            for(i=0; i<10; i++){
               y++
            }
             console.log('loop completed')  
             resolve(y)
          }, 2000)
      })
    }
    
    //2. Create an async function
    async function secondFunction() {
        console.log('before promise call')
        //3. Await for the first function to complete
        let result = await firstFunction()
        console.log('promise resolved: ' + result)
        console.log('next step')
    }; 

    secondFunction()


Not:

Yapabilirsin basitçe şöyle hiçbir değeri olmayan . Benim örnekte, değeriyle Sonra ikinci işlevinde kullanabilecekleri.resolvePromiseresolve()resolvedPromisey


evet bu her zaman işe yarayacaktır. bu tür senaryolarda yer alan herkes JavaScript Promise onlar için hile yapar.
Puttamarigowda MS

5

Vaatlerle ilgili tek sorun IE'nin onları desteklememesi. Edge öyle, ancak orada çok sayıda IE 10 ve 11 var: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise (altta uyumluluk)

Yani, JavaScript tek iş parçacıklı. Eşzamansız bir arama yapmıyorsanız, tahmin edilebilir şekilde davranacaktır. Ana JavaScript iş parçacığı, bir sonrakini çalıştırmadan önce kodda göründükleri sırayla bir işlevi tamamen yürütür. Eşzamanlı işlevler için garanti sırası önemsizdir - her işlev çağrıldığı sırayla tamamen yürütülür.

Senkron işlevi bir atomik iş birimi olarak düşünün . Ana JavaScript iş parçacığı, ifadeleri kodda göründüğü sırayla tam olarak yürütür.

Ancak, aşağıdaki durumda olduğu gibi asenkron çağrıyı atın:

showLoadingDiv(); // function 1

makeAjaxCall(); // function 2 - contains async ajax call

hideLoadingDiv(); // function 3

Bu istediğinizi yapmaz . İşlev 1, işlev 2 ve işlev 3'ü anında yürütür. Div yanıp söner ve ajax çağrısı makeAjaxCall()geri döndü bile, neredeyse tamamlanmadı . KOMPLİKASYON , makeAjaxCall()çalışmalarını ana JavaScript iş parçacığının her dönüşünde yavaş yavaş gelişmiş parçalara böldü - asenkron olarak davranıyor. Ancak aynı ana iplik, bir sıkma / çalıştırma sırasında senkron bölümleri hızlı ve tahmin edilebilir bir şekilde yürüttü.

Yani, onu ele alma şeklim : Dediğim gibi bu fonksiyon atomik iş birimidir. İşlev 1 ve 2 kodunu birleştirdim - işlev 1 kodunu, zaman uyumsuz çağrıdan önce işlev 2'ye koydum. İşlev 1'den kurtuldum. Zaman uyumsuz çağrıyı içeren ve dahil olan her şey sırasıyla tahmin edilebilir şekilde yürütülür.

Daha sonra, eşzamansız arama tamamlandığında, ana JavaScript iş parçacığının birkaç kez döndükten sonra, işlev 3'ü çağırmasını sağlayın. Bu, siparişi garanti eder . Örneğin, ajax ile, onreadystatechange olay işleyicisi birden çok kez çağrılır. Tamamlandığını bildirdiğinde, istediğiniz son işlevi çağırın.

Daha karışık olduğuna katılıyorum. Ben simetrik kod sahip olmak gibi, fonksiyonları bir şey yapmak (ya da ona yakın) gibi ve herhangi bir şekilde (telefon arayan bir bağımlılık oluşturma) sorumlu olmak için ajax çağrısı sahip sevmiyorum. ANCAK, senkronize bir fonksiyona gömülü bir asenkron çağrı ile, yürütme sırasını garanti etmek için uzlaşmalar yapılmalıdır. Ve böylece hiçbir söz IE 10 kodlamak zorunda.

Özet : Senkronize çağrılar için sipariş garantisi önemlidir. Her işlev, çağrıldığı sırayla tam olarak yürütülür. Eşzamansız çağrıya sahip bir işlev için, siparişi garanti etmenin tek yolu, eşzamansız çağrının ne zaman tamamlandığını izlemek ve bu durum algılandığında üçüncü işlevi çağırmaktır.

JavaScript konuları hakkında bir tartışma için bkz. Https://medium.com/@francesco_rizzi/javascript-main-thread-dissected-43c85fce7e23 ve https://developer.mozilla.org/en-US/docs/Web/JavaScript/ EventLoop

Ayrıca, bu konuda benzer, yüksek puan alan bir soru: 3 işlevi birbiri ardına yürütmek için nasıl çağırmalıyım?


8
Downvoter'lara bu cevapta neyin yanlış olduğunu bilmek isterdim.
kermit

0

Bir zincirde birkaç operasyon yürütmem gerektiğinden bu ortaya çıktım.

<button onclick="tprom('Hello Niclas')">test promise</button>

<script>
    function tprom(mess) {
        console.clear();

        var promise = new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve(mess);
            }, 2000);
        });

        var promise2 = new Promise(async function (resolve, reject) {
            await promise;
            setTimeout(function () {
                resolve(mess + ' ' + mess);
            }, 2000);
        });

        var promise3 = new Promise(async function (resolve, reject) {
            await promise2;
            setTimeout(function () {
                resolve(mess + ' ' + mess+ ' ' + mess);
            }, 2000);
        });

        promise.then(function (data) {
            console.log(data);
        });

        promise2.then(function (data) {
            console.log(data);
        });

        promise3.then(function (data) {
            console.log(data);
        });
    }

</script>

Belirtilen snippet'in açıklaması için lütfen en az bir üst düzey yorum ekleyin. Bu, kodunuzun sorunu nasıl ele aldığını açıklamak içindir.
Soğuk Cerberus

Sorun ilkinden sonra ikinci bir işlevi çalıştırmaktı. İlk fonksiyondan sonra ikinci bir fonksiyonun nasıl çalıştırıldığını ve ikinci fonksiyondan sonra üçüncü bir fonksiyonun nasıl çalıştırılacağını gösteririm. Hangi kodun gerekli olduğunu anlamak için üç işlev yaptım.
Niclas Arnberger

0

Ana eğlenceniz önce arayacak, daha sonra bir sonraki eğlenceniz arayacak.

async firstFunction() {
            const promise = new Promise((resolve, reject) => {
                for (let i = 0; i < 5; i++) {
                    // do something
                    console.log(i);
                    if (i == 4) {
                        resolve(i);
                    }
                }
            });
            const result = await promise;
        }

        second() {
            this.firstFunction().then( res => {
                // third function call do something
                console.log('Gajender here');
            });
        }
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.