Wanneer is JavaScript synchroon?

Ik heb de indruk gehad dat JavaScript altijd asynchroon was. Ik heb echter geleerd dat er situaties zijn waarin dit niet het geval is (bijv. DOM-manipulaties). Is er ergens een goede referentie over wanneer het synchroon zal zijn en wanneer het asynchroon zal zijn? Heeft jQuery hier überhaupt invloed op?


Antwoord 1, autoriteit 100%

JavaScript is altijd synchroon en single-threaded. Als u een JavaScript-codeblok op een pagina uitvoert, wordt er momenteel geen ander JavaScript op die pagina uitgevoerd.

JavaScript is alleen asynchroon in die zin dat het bijvoorbeeld Ajax-aanroepen kan maken. De Ajax-aanroep stopt met uitvoeren en andere code kan worden uitgevoerd totdat de aanroep terugkeert (met succes of anderszins), waarna de callback synchroon loopt. Er wordt op dit moment geen andere code uitgevoerd. Het onderbreekt geen andere code die momenteel wordt uitgevoerd.

JavaScript-timers werken met hetzelfde soort terugbellen.

Het beschrijven van JavaScript als asynchroon is misschien misleidend. Het is nauwkeuriger om te zeggen dat JavaScript synchroon en single-threaded is met verschillende callback-mechanismen.

jQuery heeft een optie op Ajax-aanroepen om ze synchroon te maken (met de optie async: false). Beginners kunnen in de verleiding komen om dit verkeerd te gebruiken omdat het een meer traditioneel programmeermodel mogelijk maakt waar men misschien meer aan gewend is. De reden dat het problematisch is, is dat deze optie alleJavaScript op de pagina blokkeert totdat deze is voltooid, inclusief alle gebeurtenishandlers en timers.


Antwoord 2, autoriteit 82%

JavaScript is single threaded en heeft een synchroon uitvoeringsmodel. Single threaded betekent dat er één commando tegelijk wordt uitgevoerd. Synchroon betekent één voor één, d.w.z. één regel code wordt tegelijk uitgevoerd om de code te laten verschijnen. Dus in JavaScript gebeurt één ding tegelijk.

Context van uitvoering

De JavaScript-engine werkt samen met andere engines in de browser.
In de JavaScript-uitvoeringsstapel bevindt zich onderaan een globale context en wanneer we functies aanroepen, creëert de JavaScript-engine nieuwe uitvoeringscontexten voor respectieve functies. Wanneer de aangeroepen functie de uitvoeringscontext verlaat, wordt deze van de stapel gehaald, en vervolgens wordt de volgende uitvoeringscontext weergegeven, enzovoort…

Bijvoorbeeld

function abc()
{
   console.log('abc');
}
function xyz()
{
   abc()
   console.log('xyz');
}
var one = 1;
xyz();

In de bovenstaande code wordt een globale uitvoeringscontext gemaakt en in deze context wordt var oneopgeslagen en de waarde ervan is 1… wanneer de xyz()-aanroep wordt aangeroepen, dan wordt een nieuwe uitvoeringscontext zal worden gemaakt en als we een variabele in de xyz-functie hadden gedefinieerd, zouden die variabelen worden opgeslagen in de uitvoeringscontext van xyz(). In de xyz-functie roepen we abc() aan en dan wordt de abc()-uitvoeringscontext gemaakt en op de uitvoeringsstapel geplaatst… Als abc() nu klaar is, wordt de context uit de stapel gehaald, dan wordt de xyz()-context eruit gehaald stapel en dan wordt de globale context weergegeven…

Nu over asynchrone callbacks; asynchroon betekent meer dan één tegelijk.

Net als de uitvoeringsstapel is er de Event Queue. Als we op de hoogte willen worden gehouden van een gebeurtenis in de JavaScript-engine, kunnen we naar die gebeurtenis luisteren en die gebeurtenis wordt in de wachtrij geplaatst. Bijvoorbeeld een Ajax-verzoekgebeurtenis of HTTP-verzoekgebeurtenis.

Telkens wanneer de uitvoeringsstapel leeg is, zoals weergegeven in het bovenstaande codevoorbeeld, kijkt de JavaScript-engine periodiek naar de gebeurteniswachtrij en kijkt of er een gebeurtenis is waarover moet worden geïnformeerd. In de wachtrij waren bijvoorbeeld twee gebeurtenissen, een ajax-verzoek en een HTTP-verzoek. Het kijkt ook of er een functie is die moet worden uitgevoerd op die gebeurtenistrigger… Dus de JavaScript-engine wordt op de hoogte gebracht van de gebeurtenis en kent de respectieve functie die moet worden uitgevoerd op die gebeurtenis… Dus de JavaScript-engine roept de handlerfunctie, in het voorbeeldgeval, bijv AjaxHandler() wordt aangeroepen en zoals altijd wanneer een functie wordt aangeroepen, wordt de uitvoeringscontext in de uitvoeringscontext geplaatst en nu is de uitvoering van de functie voltooid en wordt de gebeurtenis ajax-aanvraag ook verwijderd uit de gebeurteniswachtrij… Wanneer AjaxHandler() de uitvoeringsstapel is leeg, dus de engine kijkt opnieuw naar de gebeurteniswachtrij en voert de gebeurtenishandlerfunctie uit van het HTTP-verzoek dat als volgende in de wachtrij stond. Het is belangrijk om te onthouden dat de gebeurteniswachtrij alleen wordt verwerkt als de uitvoeringsstapel leeg is.

Zie bijvoorbeeld de onderstaande code die uitleg geeft over de uitvoeringsstack en de afhandeling van de gebeurteniswachtrij door de Javascript-engine.

function waitfunction() {
    var a = 5000 + new Date().getTime();
    while (new Date() < a){}
    console.log('waitfunction() context will be popped after this line');
}
function clickHandler() {
    console.log('click event handler...');   
}
document.addEventListener('click', clickHandler);
waitfunction(); //a new context for this function is created and placed on the execution stack
console.log('global context will be popped after this line');

en

<html>
    <head>
    </head>
    <body>
        <script src="program.js"></script>
    </body>
</html>

Nu lopen de webpagina en klik op de pagina, en zie de uitgang op de console. De output zal

waitfunction() context will be popped after this line
global context will be emptied after this line
click event handler...

De JavaScript-engine is het uitvoeren van de code synchroon zoals uitgelegd in de uitvoering context gedeelte, wordt de browser asynchroon om de dingen in geval wachtrij. Dus de functies die een zeer lange tijd te voltooien kunnen hanteren interrupt evenement. Dingen gebeuren in een browser als gebeurtenissen worden behandeld op deze manier door JavaScript, als er een luisteraar hoort te lopen, zal de motor draaien wanneer de uitvoering stapel leeg is. En evenementen worden verwerkt in de volgorde waarin ze gebeuren, dus het asynchrone deel gaat over wat er gebeurt buiten de motor dus wat moet de motor doen als degenen die buiten gebeurtenissen gebeuren.

Dus JavaScript is altijd synchroon.


Antwoord 3, Autoriteit 33%

JavaScript is single-threaded, en de hele tijd werk je aan een normaal synchroon uitvoeren code-flow.

Een goed voorbeeld van de asynchrone gedrag dat JavaScript kan hebben zijn gebeurtenissen (interactie met de gebruiker, Ajax verzoek resultaten, etc) en timers, in principe acties die zouden kunnen gebeuren op elk moment.

Ik zou u aanraden om een ​​kijkje naar het volgende artikel te geven:

Dat artikel helpt je het single-threaded karakter van JavaScript te begrijpen en hoe timers intern werken en hoe asynchrone JavaScript-uitvoering werkt.


Antwoord 4, autoriteit 5%

Voor iemand die echt begrijpt hoe JS werkt, lijkt deze vraag misschien niet zo, maar de meeste mensen die JS gebruiken hebben niet zo’n diep inzicht (en hebben het niet per se nodig) en voor hen is dit een nogal verwarrend punt , ik zal proberen vanuit dat perspectief te antwoorden.

JS is synchroon in de manier waarop de code wordt uitgevoerd. elke regel loopt alleen na de regel voordat deze is voltooid en als die regel daarna een functie aanroept, is deze voltooid enz…

Het belangrijkste punt van verwarring komt voort uit het feit dat uw browser JS kan vertellen om op elk moment meer code uit te voeren (vergelijkbaar met hoe u meer JS-code op een pagina vanaf de console kunt uitvoeren). Als voorbeeld heeft JS Callback-functies waarvan het doel is om JS asynchroon te laten GEDRAGEN, zodat verdere delen van JS kunnen draaien in afwachting van een uitgevoerde JS-functie (IE een GET-aanroep) om een antwoord, zal JS blijven draaien totdat de browser een antwoord heeft. Op dat moment zal de gebeurtenislus (browser) de JS-code uitvoeren die de callback-functie aanroept.

Aangezien de gebeurtenislus (browser) meer JS kan invoeren om op elk punt uit te voeren, is JS asynchroon (de belangrijkste dingen die ervoor zorgen dat een browser JS-code invoert, zijn time-outs, callbacks en gebeurtenissen)

Ik hoop dat dit duidelijk genoeg is om iemand te helpen.


Antwoord 5, autoriteit 2%

Definitie

De term ‘asynchroon’ kan in enigszins verschillende betekenissen worden gebruikt, wat hier resulteert in schijnbaar tegenstrijdige antwoorden, terwijl dat in werkelijkheid niet het geval is. Wikipedia on Asynchronyheeft deze definitie:

Asynchronie, in computerprogrammering, verwijst naar het optreden van gebeurtenissen die onafhankelijk zijn van de hoofdprogrammastroom en manieren om met dergelijke gebeurtenissen om te gaan. Dit kunnen “externe” gebeurtenissen zijn, zoals de aankomst van signalen, of acties die worden geïnitieerd door een programma die gelijktijdig met de uitvoering van het programma plaatsvinden, zonder dat het programma blokkeert om op resultaten te wachten.

niet-JavaScript-code kan dergelijke “buiten”-gebeurtenissen in een wachtrij plaatsen in een aantal gebeurtenissen-wachtrijen van JavaScript. Maar dat is zo ver als het gaat.

Geen voorrang

Er is geen externe onderbrekingvan het uitvoeren van JavaScript-code om een andere JavaScript-code in uw script uit te voeren. Stukken JavaScript worden na elkaar uitgevoerd en de volgorde wordt bepaald door de volgorde van gebeurtenissen in elke gebeurteniswachtrij en de prioriteit van die wachtrijen.

Je kunt er bijvoorbeeld absoluut zeker van zijn dat geen enkel ander JavaScript (in hetzelfde script) ooit zal worden uitgevoerd terwijl het volgende stuk code wordt uitgevoerd:

let a = [1, 4, 15, 7, 2];
let sum = 0;
for (let i = 0; i < a.length; i++) {
    sum += a[i];
}

Met andere woorden, er is geen voorheffingin JavaScript. Wat er ook in de wachtrijen voor gebeurtenissen staat, de verwerking van die gebeurtenissen zal moeten wachten tot een dergelijk stukje code is voltooid. De EcmaScript-specificatie zegt in sectie 8.4 Taken en takenwachtrijen:

De uitvoering van een taak kan alleen worden gestart als er geen actieve uitvoeringscontext is en de uitvoeringscontextstack leeg is.

Voorbeelden van asynchronie

Zoals anderen al hebben geschreven, zijn er verschillende situaties waarin asynchronie een rol speelt in JavaScript, en het gaat altijd om een gebeurteniswachtrij, die alleen kan resulteren in JavaScript-uitvoering als er geen andere JavaScript-code wordt uitgevoerd:

  • setTimeout(): de agent (bijv. browser) zal een gebeurtenis in een gebeurteniswachtrij plaatsen wanneer de time-out is verlopen. Het bewaken van de tijd en het plaatsen van de gebeurtenis in de wachtrij gebeurt door niet-JavaScript-code, en je zou je dus kunnen voorstellen dat dit gelijktijdig gebeurt met de mogelijke uitvoering van een bepaalde JavaScript-code. Maar de callback naar setTimeoutkan alleen worden uitgevoerd wanneer de momenteel uitgevoerde JavaScript-code is voltooid en de juiste gebeurteniswachtrij wordt gelezen.

  • fetch(): de agent zal OS-functies gebruiken om een HTTP-verzoek uit te voeren en te controleren op binnenkomende reacties. Nogmaals, deze niet-JavaScript-taak kan parallel lopen met een JavaScript-code die nog steeds wordt uitgevoerd. Maar de procedure voor het oplossen van de belofte, die de belofte oplost die wordt geretourneerd door fetch(), kan alleen worden uitgevoerd als het JavaScript dat momenteel wordt uitgevoerd, is voltooid.

  • requestAnimationFrame(): De renderingsmotor van de browser (niet-javascript) plaatst een evenement in de JavaScript-wachtrij wanneer deze klaar is om een ​​verfbewerking uit te voeren. Wanneer JavaScript-gebeurtenis wordt verwerkt, wordt de callback-functie uitgevoerd.

  • queueMicrotask(): Plaats onmiddellijk een evenement in de Microtask-wachtrij. De callback wordt uitgevoerd wanneer de oproepstack leeg is en dat gebeurtenis wordt geconsumeerd.

Er zijn veel meer voorbeelden, maar al deze functies worden geleverd door de gastheeromgeving, niet door Core Ecmascript. Met Core Ecmascript kunt u synchrroon een evenement plaatsen in een belofingsbaanwachtrij met Promise.resolve().

Taalconstructies

Ecmascript biedt verschillende taalconstructen om het asynchronische patroon te ondersteunen, zoals yield, async, await. Maar laat er geen vergissing zijn: Geen JavaScript-code is onderbroken door een externe gebeurtenis. De “onderbreking” die yielden awaitlijken te bieden is slechts een gecontroleerde, vooraf gedefinieerde manier om terug te keren van een functie-oproep en het herstel van de uitvoering van de uitvoering van de JS-code (in het geval van yield), of de gebeurteniswachtrij (in het geval van await).

DOM Evenement Handling

Wanneer JavaScript-code toegang heeft tot de DOM-API, kan dit in sommige gevallen de DOM API een of meer synchrone meldingen veroorzaken. En als uw code een gebeurtenishandler heeft, luistert het daaraan.

Dit kan tegenkomen als prepptieve concurrency, maar het is niet: zodra uw evenementhandler (s) retourneren (en), de DOM-API uiteindelijk ook terugkeert, en de originele JavaScript-code zal doorgaan.

In andere gevallen verzendt de DOM API een gebeurtenis gewoon in de juiste gebeurteniswachtrij en neemt JavaScript het op zodra de aanroepstack is geleegd.

Zie synchrone en asynchrone gebeurtenissen


Antwoord 6

“Ik heb de indruk gehad dat JavaScript altijd al was
asynchroon”

Je kunt JavaScript op een synchrone of een asynchrone manier gebruiken. In feite heeft JavaScript echt goede asynchrone ondersteuning. Ik heb bijvoorbeeld code waarvoor een databaseverzoek is vereist. Ik kan dan andere code uitvoeren, niet dependentop dat verzoek, terwijl ik wacht tot dat verzoek is voltooid. Deze asynchrone codering wordt ondersteund met beloften, asynchrone/wachten, enz. Maar als je geen leuke manier nodig hebt om lange wachttijden af te handelen, gebruik dan gewoon JS synchroon.

Wat bedoelen we met ‘asynchroon’. Nou, het betekent niet multi-threaded, maar beschrijft eerder een niet-afhankelijke relatie. Bekijk deze afbeelding van dit populaire antwoord:

        A-Start ------------------------------------------ A-End   
           | B-Start ----------------------------------------- | --- B-End   
           |    |      C-Start ------------------- C-End      |      |   
           |    |       |                           |         |      |
           V    V       V                           V         V      V      
1 thread->|<-A-|<--B---|<-C- | -A- | -C- | --A-- | -B- | --C-->|---A---->|--B-->| 

We zien dat een toepassing met één thread async kan hebben. Het werk in functie A is niet afhankelijk van het voltooien van functie B, en terwijl functie A begon vóór functie B, kan functie A op een later tijdstip en op dezelfde thread worden voltooid.

Dus, alleen omdat JavaScript één opdracht tegelijk uitvoert, op een enkele thread, volgt hier niet uit dat JavaScript alleen als een synchrone taal kan worden gebruikt.

“Is er ergens een goede referentie over wanneer het synchroon zal zijn en wanneer het asynchroon zal zijn”

Ik vraag me af of dit de kern van je vraag is. Ik neem aan dat je bedoelt hoe weet je of een code die je aanroept async of sync is. Dat wil zeggen, zal de rest van je code weglopen en iets doen terwijl je wacht op een resultaat? Uw eerste controle moet de documentatie zijn voor de bibliotheek die u gebruikt. Knooppuntmethoden hebben bijvoorbeeld duidelijke namen zoals readFileSync. Als de documentatie niet goed is, is er veel hulp hier op SO. Bv:

Hoe weet ik of een functie async is?


Antwoord 7

Is synchroon in alle gevallen.

Voorbeeld van blokkeerthread met Promises:

 const test = () => new Promise((result, reject) => {
    const time = new Date().getTime() + (3 * 1000);
    console.info('Test start...');
    while (new Date().getTime() < time) {
      // Waiting...
    }
    console.info('Test finish...');
  });
  test()
    .then(() => console.info('Then'))
    .finally(() => console.info('Finally'));
  console.info('Finish!');

De uitvoer zal zijn:

Test start...
Test finish...
Finish!

Other episodes