De juiste manier om te wachten tot een functie is voltooid voordat u verdergaat?

Ik heb twee JS-functies. De een roept de ander. Binnen de aanroepfunctie zou ik de andere willen bellen, wachten tot die functie is afgelopen en dan verder gaan. Dus bijvoorbeeld/pseudocode:

function firstFunction(){
    for(i=0;i<x;i++){
        // do something
    }
};
function secondFunction(){
    firstFunction()
    // now wait for firstFunction to finish...
    // do something else
};

Ik heb deze oplossing bedacht, maar weet niet of dit een slimme manier is om dit aan te pakken.

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

Is dat legitiem? Is er een elegantere manier om ermee om te gaan? Misschien met jQuery?


Antwoord 1, autoriteit 100%

Een manier om met dit soort asynchroon werk om te gaan, is door een callback-functie te gebruiken, bijvoorbeeld:

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

Per suggestie @Janaka Pushpakumara’s, kunt u nu gebruik maken van de pijl functies om hetzelfde te bereiken. Bijvoorbeeld:

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


Update: antwoordde ik dit al geruime tijd geleden, en echt wilt bijwerken. Terwijl callbacks zijn absoluut prima, in mijn ervaring hebben ze de neiging om te resulteren in een code die is moeilijk te lezen en te onderhouden. Er zijn situaties waarin ik dacht dat ze gebruiken nog steeds, zoals door te geven aan de gang evenementen en dergelijke als parameters. Deze update is alleen maar om alternatieven te benadrukken.

Ook de oorspronkelijke vraag niet specificallty vermelding asynchrone, dus in het geval iemand is in de war, als uw functie is synchroon, is zal blok als ze worden opgeroepen. Bijvoorbeeld:

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

Als hoewel zoals wordt gesuggereerd de functie asynchroon is, de manier waarop ik de neiging om te gaan met al mijn asynchrone werk vandaag is met async / af te wachten. Bijvoorbeeld:

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

IMO, async / af te wachten maakt uw code veel beter leesbaar dan het gebruik van beloften rechtstreeks (de meeste van de tijd). Als u nodig hebt om het vangen van fouten af ​​te handelen, gebruik het dan met try / catch. Lees erover more here: https: //developer.mozilla .org / en-US / docs / Web / JavaScript / Reference / verklaringen / async_function .


Antwoord 2, Autoriteit 42%

Gebruik async / af te wachten:

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

gebruik vervolgens wait in uw andere functie om te wachten tot deze terugkeert:

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

Antwoord 3, autoriteit 29%

Het lijkt erop dat u hier een belangrijk punt mist: JavaScript is een uitvoeringsomgeving met één thread. Laten we nog eens naar je code kijken, let op: ik heb alert("Here")toegevoegd:

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

Je hoeft niet te wachten op isPaused. Wanneer u de waarschuwing “Hier” ziet, is isPausedal falseen is firstFunctionteruggekeerd. Dat komt omdat je niet kunt “meegeven” vanuit de for-lus (// do something), de lus mag niet worden onderbroken en moet eerst volledig worden voltooid (meer details: Javascript-thread-handling en race-voorwaarden).

Dat gezegd hebbende, kunt u de code nog steeds insluiten in firstFunctionom asynchroon te zijn en te gebruiken of belooft u de beller op de hoogte te stellen. U zou moeten opgeven bij forlus en het simuleren met ifin plaats daarvan (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);
    });
}

Aan een zijkant opmerking, JavaScript 1.7 heeft yieldtrefwoord als onderdeel van Generatoren . Dat zal toestaan ​​dat “Punch” asynchrone gaten in anderszins synchrone JavaScript-code stroom (meer details en een voorbeeld ). De browsersteun voor generatoren is echter momenteel beperkt tot Firefox en Chrome, AFAIK.


Antwoord 4, Autoriteit 22%

Een elegante manier om te wachten op één functie om het eerst te voltooien is het gebruiken beloftes met Async / wachten -functie.


  1. In de eerste plaats, maak een Promise .
    De functie die ik heb gemaakt zal worden afgerond na 2 seconden. ik gebruikte
    setTimeoutin om de situatie aan te tonen waar de
    instructies zou enige tijd duren om uit te voeren.
  2. Voor de tweede functie, die u kunt gebruiken
    asynchrone / af te wachten
    functie waar u awaitvoor de eerste functie om complete
    voordat u verder gaat met de instructies.

Voorbeeld:

   //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()

Antwoord 5, Autoriteit 6%

Ik vraag me af waarom niemand heeft gezegd dit eenvoudig patroon? :

(function(next) {
  //do something
  next()
}(function() {
  //do some more
}))

Time-outs gebruiken om blindelings te wachten is een slechte gewoonte; en het betrekken van beloften voegt alleen maar meer complexiteit toe aan de code. In het geval van OP:

(function(next) {
  for(i=0;i<x;i++){
    // do something
    if (i==x-1) next()
  }
}(function() {
  // now wait for firstFunction to finish...
  // do something else
}))

een kleine demo -> http://jsfiddle.net/5jdeb93r/


Antwoord 6, autoriteit 3%

Het enige probleem met beloften is dat IE ze niet ondersteunt. Edge wel, maar er zijn genoeg IE 10 en 11 beschikbaar: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise(compatibiliteit onderaan)

Dus JavaScript is single-threaded. Als u geen asynchroon gesprek voert, gedraagt het zich voorspelbaar. De belangrijkste JavaScript-thread voert de ene functie volledig uit voordat de volgende wordt uitgevoerd, in de volgorde waarin ze in de code verschijnen. Het garanderen van de volgorde voor synchrone functies is triviaal – elke functie wordt volledig uitgevoerd in de volgorde waarin deze werd aangeroepen.

Denk aan de synchrone functie als een atomaire werkeenheid. De belangrijkste JavaScript-thread zal het volledig uitvoeren, in de volgorde waarin de instructies in de code verschijnen.

Maar gooi de asynchrone oproep erin, zoals in de volgende situatie:

showLoadingDiv(); // function 1
makeAjaxCall(); // function 2 - contains async ajax call
hideLoadingDiv(); // function 3

Dit doet niet wat u wilt . Het voert onmiddellijk de functie 1, functie 2 en functie uit 3. Laden div-flitsen en het is verdwenen, terwijl de AJAX-oproep niet bijna is voltooid, hoewel makeAjaxCall()is teruggekeerd. De complicatie is dat makeAjaxCall()zijn werk heeft verbroken in brokken die bij beetje van de main bij beetje worden gevorderd Javascript-thread – het gedraagt ​​zich zoychroon. Maar diezelfde hoofddraad, tijdens één spin / run, voerde de synchrone porties snel en voorspelbaar uit.

Dus, de manier waarop ik het behandelde : zoals ik zei dat de functie de atoomeenheid van werk is. Ik gecombineerd de functie van de functie 1 en 2 – Ik heb de functiecode 1 in functie 2 geplaatst, vóór de ASYLCH-oproep. Ik raakte van functie 1. Alles tot en met de asynchrone gesprek voert voorspelbaar, in volgorde uit.

Dan, wanneer de asynchrone gesprek afrondt, na een aantal spins van de hoofd Javascript-thread, belt IT-functie 3. Dit garandeert de bestelling . Bijvoorbeeld, met Ajax, wordt de onreadystatechange-gebeurtenishandler meerdere keren genoemd. Wanneer het meldt dat het is voltooid, bel dan de eindfunctie die u wilt.

Ik ben het ermee eens dat het Messier is. Ik vind het leuk om code te hebben Symmetric, ik vind het leuk om functies te hebben die één ding (of dicht bij het) doen, en ik vind het niet leuk om de Ajax-oproep op geen enkele manier te laten zijn verantwoordelijk voor het display (het creëren van een afhankelijkheid van de beller). Maar met een asynchroon gesprek ingebed in een synchrone functie, moeten compromissen worden gemaakt om de uitvoering van uitvoering te garanderen. En ik moet voor IE 10 zijn, dus geen beloften.

Samenvatting: Voor synchrone gesprekken is het garanderen van de orde triviaal. Elke functie wordt volledig uitgevoerd in de volgorde waarin deze is aangeroepen. Voor een functie met een asynchrone aanroep is de enige manier om de volgorde te garanderen, te controleren wanneer de asynchrone aanroep is voltooid en de derde functie aan te roepen wanneer die status wordt gedetecteerd.

Voor een bespreking van JavaScript-threads, zie: https://medium. com/@francesco_rizzi/javascript-main-thread-dissected-43c85fce7e23en https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

Ook nog een vergelijkbare, hoog gewaardeerde vraag over dit onderwerp: Hoe moet ik 3 functies aanroepen om ze een voor een uit te voeren?


Antwoord 7, autoriteit 2%

Kan Belofte

use gebruiken

Een belofte is een JavaScript-constructie die een toekomstige onbekende waarde vertegenwoordigt. Dit kan het resultaat zijn van een API-aanroep, of het kan een foutobject zijn van een mislukt netwerkverzoek. Je krijgt gegarandeerd iets.

const promise = new Promise((resolve, reject) => {
    // Make a network request
   if (yourcondition=="value") {
   } else {
      reject(error);
   }
})
promise.then(res => {
    //if not rejected, code
}).catch(err => {
    //return false; 
})

Een belofte kan hebben

vervuld – actie succesvol voltooid

afgewezen – actie mislukt

in behandeling – geen van beide acties is voltooid

Settled – is voldaan of verworpen


Antwoord 8

Uw hoofdplezier belt Firstfun en vervolgens op het resultaat, uw volgende plezier zal bellen.

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

Antwoord 9

Dit wat ik bedacht, omdat ik verschillende bewerkingen in een ketting nodig heb.

<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>

Other episodes