Stelt dit paar AJAX een race-conditie voor?

Ik maak een website met nieuwsartikelen. Deze artikelen verschijnen in twee kolommen onderaan de pagina. Er zal onderaan een knop zijn om extra nieuwsverhalen te laden. Dat betekent dat ik moet kunnen specificeren welk nieuwsverhaal ik moet laden. Server-kant, ik ben dit simpelweg aan het implementeren met een LIMIT-clausule in mijn SQL-instructie, die de: first parameter als volgt levert:

SELECT * 
FROM news 
ORDER BY `date` DESC 
LIMIT :first, 1

Dit betekent dat ik, client-side, moet bijhouden hoeveel nieuwsitems ik heb geladen. Ik heb dit geïmplementeerd door de functie te hebben om nieuwe informatie te laden in een object met een eigenschap die het aantal geladen items bevat. Ik ben bang dat dit op de een of andere manier een raceconditie is die ik niet zie, waar mijn loadNewInformation() tweemaal wordt aangeroepen voordat het nummer wordt verhoogd. Mijn code is als volgt:

var News = {

    newInfoItems: 0,

    loadNewInformation: function(side) {
        this.newInfoItems += 1;
        jQuery.get(
            '/api/news/'+ (this.newInfoItems - 1),
            function(html) {
                jQuery('div.col'+side).append(html);
            }
        );
    }
}

Bij het laden van de pagina wordt dit op de volgende manier aangeroepen:

News.loadNewInformation('left');
News.loadNewInformation('right');

Ik had dit op zo'n manier kunnen implementeren dat de succeshandler van een eerste oproep een ander AJAX-verzoek deed voor de tweede, wat duidelijk geen race-conditie zou zijn ... maar dit lijkt slordige code. Gedachten?

2
Ik vind het echt leuk wat je hier hebt gedaan. Het is nuttig genoeg om te proberen het algemene idee van een AJAX-wachtrij naar zijn eigen object te brengen, zodat elke klasse AJAX-verzoeken in de wachtrij kan worden geplaatst terwijl andere asynchroon kunnen worden uitgevoerd.
toegevoegd de auteur rybosome, de bron
Ik heb een jsfiddle gemaakt met volledig ongeteste code die mogelijk iets meer doet zoals je wilt. Laat het me weten als je hulp nodig hebt om het door te lezen. Bijgewerkt
toegevoegd de auteur JesseBuesking, de bron

5 antwoord

Ja, technisch gezien zou dit een soort race-conditie kunnen creëren. De oproepen zijn asynchroon en als de eerste om de een of andere reden werd opgehouden, kon de tweede als eerste terugkeren.

Omdat u echter niet veel doet aan uw callback-functies die afhankelijk zijn van de aanwezigheid van de andere 'kant' die wordt bevolkt, zie ik niet waar het u teveel verdriet zou moeten bezorgen.

1
toegevoegd
Is er een elegantere manier om hiermee om te gaan?
toegevoegd de auteur rybosome, de bron
Ik vermoed dat ik met meer 'elegant' bedoelde iets dat een oudere-dev niet zou bespotten als ze het zagen. ;) Ik maak me geen zorgen als de ene voor de andere verschijnt, omdat de pagina er nog steeds goed uitziet. Ze worden gepropt in absoluut gepositioneerde divs, dus hun lay-out wordt niet beïnvloed door de aanwezigheid van de ander.
toegevoegd de auteur rybosome, de bron
@Ryan - Kun je 'elegant' uitleggen? Bedoel je "is er een manier om dit te benaderen, dat ervoor zorgt dat ze altijd bevolkt worden in de volgorde waarin ik ze bel?" U gaf al aan dat u zag hoe u dat kon doen door de tweede load in de callback-functie van de eerste aan te roepen. In het ideale geval wil je ze echter niet samenvoegen. U zou een wachtrij van openstaande aanvragen kunnen maken, deze kunnen ontslaan en vervolgens kunnen binden aan ajaxStop om te wachten tot de eerste terugkeert voordat de tweede wordt geactiveerd.
toegevoegd de auteur g.d.d.c, de bron

(Ja, er is een race-conditie.)

Alleen JavaScript adresseren

Alle JavaScript-code op een pagina (exclusief webwerkers), inclusief callbacks, wordt "wederzijds exclusief" uitgevoerd.

In dit geval, omdat newInfoItems gretig geëvalueerd wordt , is het niet eens zo complex: zowel "/ api/news/0" en "/ api/news/1" worden gegarandeerd opgehaald (of mislukken) in een poging). Vergelijk het met dit:

// eager evaluation: value of url computed BEFORE request
// this is same as in example (with "fixed" increment order ;-)
// and is just to show point
var url = "/api/news/" + this.newInfoItems
this.newInfoItems += 1;
jQuery.get(url, 
    function(html) {
       //only evaluated on AJAX callback - order of callbacks
       //not defined, but will still be mutually exclusive.
        jQuery('div.col'+side).append(html);
    }
);

De volgorde waarin de AJAX-aanvragen zijn voltooid, is echter niet gedefinieerd en wordt beïnvloed door zowel de server als de browser. Verder, zoals hieronder besproken, is er geen atomische context tussen de server en individuele AJAX-verzoeken .

Het adresseren van JavaScript in context

Nu, hoewel is vastgesteld dat "/ api/news/0" en "/ api/news/1" zullen worden aangeroepen, stel je deze onwaarschijnlijke, maar theoretisch mogelijke situatie voor :

  • articles B,A exist in database
  • browser sends both AJAX requests -- asynchronously or synchronously, it doesn't matter!
  • an article is added to the database sometime between when
    • the server processes the news/0 request, and
    • the server processes the news/1 request

Dan gebeurt dit:

  • news/0 returns article B (articles B,A in database)
  • article C added
  • news/1 returns article B (articles C,B,A in database)

Let op: artikel B is twee keer geretourneerd! Oeps :)

Dus, terwijl de race-conditie "redelijk onwaarschijnlijk lijkt" , bestaat deze. Een vergelijkbare raceconditie (met verschillende resultaten) kan optreden als news/1 wordt verwerkt vóór news/0 en (nogmaals) een artikel wordt toegevoegd tussen de verzoeken: er is geen atomaire garantie in beide gevallen!

(De bovenstaande raceconditie zou waarschijnlijker zijn als de AJAX-aanvragen in-series worden uitgevoerd, omdat de tijd voor het toevoegen van een nieuw artikel wordt verhoogd.)

Mogelijke oplossing

Overweeg om de artikelen say, n (2 is ok!) In een single -verzoek (bijvoorbeeld "/ api/latest/n") op te halen en de artikelen vervolgens naar wens op te stellen in de callback . Bijvoorbeeld de eerste helft van de artikelen aan de linkerkant en de tweede helft aan de rechterkant, of wat dan ook passend is.

Naast het elimineren van de specifieke raceconditie hierboven door het maken van het enkele verzoek een atomaire actie - met betrekking tot artikeltoevoegingen - zal dit ook resulteren in minder netwerkverkeer en minder werk voor de server.

De ophaalactie voor de API kan er dan als volgt uitzien:

SELECT * 
FROM news 
ORDER BY `date` DESC 
LIMIT :n

Happy codering.

1
toegevoegd

Shouldn't be any race conditions, must be something else wrong in your code. The counter is incremented before your .get() call, so prior to each get the counter should be incremented correctly. Short example to demostrate that it works when called sequentially: http://jsfiddle.net/KkpwF/

0
toegevoegd
Ik heb geen fouten gezien, het werkt precies zoals verwacht. Dit was meer nieuwsgierigheid/voorzorg. Dank je wel, leuke jsfiddle! =)
toegevoegd de auteur rybosome, de bron

Volgens mij heb je een hint naar de newItemInfo -teller?

opmerkingen:

  1. U belt News.loadNewInformation tweemaal, met een callback-functie die wordt uitgevoerd bij het voltooien van AJAX.
  2. De callback-methode die u opgeeft, verandert het Nieuws -object niet.

In dit geval hoeft u zich geen zorgen te maken. De tweede aanroep van News.loadNewInformation wordt pas uitgevoerd als de eerste oproep is voltooid, dat wil zeggen, exclusief het uitvoeren van de terugbelmethode. Vandaar dat uw nieuwItemInfo -teller de juiste waarde zal bevatten.

0
toegevoegd

Probeer dit:

loadNewInformation: function(side) {
    jQuery.get(
        '/api/news/'+ (this.newInfoItems++),
        function(html) {
            jQuery('div.col'+side).append(html);
        }
    );
}
0
toegevoegd