[jQuery] 'hashChange' event fired pas na $.ajax callback

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • TheNephilim
  • Registratie: September 2005
  • Laatst online: 11-07 12:10
Een raar probleem, waar ik niet uit kom :o

De situatie in stappen.

1. var busy = true;
2. Producten ophalen
3. location.hash = hash;
4. var busy = false;

Op enkele plekken busy bekeken:

1. Before ajax, busy: true
2. Ajax request!
3. After ajax, busy: false
4. Hashchange, busy: false
5. Before ajax, busy: true
6. Ajax request!
7. After ajax, busy: false 


1. Before callback, busy = true;
2. Ajax request met parameters.
3. Done callback, busy = false;

Dan bij 4 krijgen we een hashchange, terwijl ik al voor busy = false de location.hash wijzig!

De versimpelde code:

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
var busy = false;

(function($){
    $.fn.get_products = function(args){

        var target = $(this);

        $.ajax({
            type: 'POST',
            url: ajaxurl,
            data: data,
            dataType: 'json',
            beforeSend: function (xhr) {
                busy = true;
                console.log('Before ajax, busy: '+busy);
            }
        }).done(function (response) {

            console.log('Ajax request!');

            if (response.status === "OK") {

                if (response.data.hash) {
                    location.hash = "filter:"+response.data.hash;
                } else if (location.hash) {
                    location.hash = '';
                }

                // [...] Andere shizzle

                target.html(response.data.html);

            } else {
                // Spit out response for debugging
                console.log(response);
            }

            busy = false;
            console.log('After ajax, busy: '+busy);
        });
    };
})(jQuery);


De functie get_products() word aangeroepen bij het veranderen van de opties, of als er geklikt word op volgende/vorige knoppen van de browser 'hashChange'.

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Detect a hash change, browser next of prev buttons
$(window).hashchange(function(){

    console.log('Hashchange, busy: '+busy);

    if (!busy) {
        // Check is a hash is present
        if (location.hash) {            
            // Get products by hash
            productListing.get_products({ hash: location.hash });
        } else {
            // Get products
            productListing.get_products();
        }
    }
});


Heeft iemand een idee waarom hij niks doet met deze volgorde? :'(

[ Voor 12% gewijzigd door TheNephilim op 25-01-2013 12:09 ]


Acties:
  • 0 Henk 'm!

  • jeroenikke
  • Registratie: Augustus 2003
  • Laatst online: 16:51
Worden die events niet in een andere thread uitgevoerd? Dan kan het zijn dat busy false of true is tijdens het uitvoeren van het event, afhankelijk welke thread eerst cpu time krijgt. Aangezien zo'n busy = false commando weinig cpu time inpakt, het zo goed als altijd nog zal uitgevoerd worden voordat de event-thread nog wat cpu time krijgt.

Kan je niet de hashchange event manueel oproepen net voordat je busy op false zet?

Acties:
  • 0 Henk 'm!

  • TheNephilim
  • Registratie: September 2005
  • Laatst online: 11-07 12:10
Nee voor zover ik weet is Javascript gewoon single-threaded tenzij je meer workers aanmaakt, dit is echt hier niet het geval. Volgens mij doet jQuery daar nog niet aan XD

Nope, die hashchange triggert bij de browsers volgende/vorige knoppen en dus als ik de hash aanpas in mijn ajax callback. Die moet alleen niks doen als het veranderen van de hash in een ajax request gebeurd. Alleen als de hashchange komt van vorige/volgende.

Acties:
  • 0 Henk 'm!

  • jeroenikke
  • Registratie: Augustus 2003
  • Laatst online: 16:51
Ik weet niet precies hoe het zit bij JS, het is inderdaad single threaded, maar het heeft toch een event queue, waardoor de asynchronous events niet onmiddellijk worden uitgevoerd? Of iets dergelijks, ik zou het nog eens moeten opzoeken.

EDIT: Ik moet echt studeren eigenlijk, maar ik ben toch benieud. Om toch een wilde gok te wagen:
http://javascript.info/tu...e-settimeout-func-0-trick
Als je nu die busy = false in een settimeout(function () { busy = false }, 0) zet?

Random shit testing-development 8)

[ Voor 39% gewijzigd door jeroenikke op 25-01-2013 12:22 ]


Acties:
  • 0 Henk 'm!

  • curvemod
  • Registratie: Maart 2009
  • Laatst online: 09-07 12:58
Kan je even een JS-Fiddle maken? Misschien dat dat iets meer duidelijkheid schept en het maakt het makkelijker om te testen :)

Acties:
  • 0 Henk 'm!

  • creator1988
  • Registratie: Januari 2007
  • Laatst online: 13-07 21:36
De hashchange komt pas in de volgende tick, is dus niet synchronous. Een setTimeout van 0 scheduled je busy=false code in dezelfde tick dus dat zal wel werken, maar is wel butt-ugly en je gaat gegarandeerd meer timing issues creeeren. Als deze state belangrijk is zorg er dan voor dat je hashChange function busy naar false zet.

Acties:
  • 0 Henk 'm!

  • GateKeaper
  • Registratie: April 2004
  • Laatst online: 06-07 10:13

GateKeaper

#1 Procastinator

JSFiddle: http://jsfiddle.net/7FWhA/

Gaat er op neer komen dat je je eigen "UpdateHash" functie zal moeten maken met een callback. De hashchanged event wordt pas afgeschoten als alle javascript klaar is. Beetje vergelijkbaar met "document onload"?

Als je de event handmatig "triggerd" met $.trigger('hashchange'), zie je ook dat daarna helemaal geen javascript meer wordt uitgevoerd.

Oplossing is dus eenvoudig, schrijf je iegen updateHash method, met de callback.

Acties:
  • 0 Henk 'm!

  • TheNephilim
  • Registratie: September 2005
  • Laatst online: 11-07 12:10
Bedankt voor alle reacties! De oorzaak van het probleem is nu in ieder geval helemaal duidelijk. Als ik het dus goed begrijp zit er verschil in de 'events'? De een word wel meteen uitgevoerd en de ander later?

Bedankt voor de oplossing GateKeaper, dat ga ik even proberen! ^^

Acties:
  • 0 Henk 'm!

  • GateKeaper
  • Registratie: April 2004
  • Laatst online: 06-07 10:13

GateKeaper

#1 Procastinator

Pauze is weer voorbij.. Ik zou gaan voor zoiets?

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function changeHash(newHash, onChangeCallback, noChangeCallback, forceChange) {
    console.log('Event: Hashchange, busy: '+busy); 
    var oldHash = location.hash.substr(1);
    
    if(newHash !== oldHash || forceChange) {
        location.hash = newHash;
        
        if(typeof onChangeCallback === 'function')
            onChangeCallback();
    }
    else {
        if(typeof noChangeCallback === 'function')
            noChangeCallback();
    }
}

...

console.log('start update hash');
                    
changeHash('filter:', function() {
    console.log('get products by hash');
});
 
changeHash('filter:', function() {
    console.log('get products (no hash)');
}, function() {
    console.log('nothing to get, didn\'t change');
});
                    
console.log('end update hash');


start update hash
Event: Hashchange, busy: true
get products by hash
Event: Hashchange, busy: true
nothing to get, didn't change
end update hash
After ajax, busy: false 

[ Voor 14% gewijzigd door GateKeaper op 25-01-2013 13:34 ]


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 04-06 11:57
TheNephilim schreef op vrijdag 25 januari 2013 @ 12:45:
Bedankt voor alle reacties! De oorzaak van het probleem is nu in ieder geval helemaal duidelijk. Als ik het dus goed begrijp zit er verschil in de 'events'? De een word wel meteen uitgevoerd en de ander later?

Bedankt voor de oplossing GateKeaper, dat ga ik even proberen! ^^
Alle javascript events, incl. je hashchange event, worden pas uitgevoerd wanneer de browser (en dus je javascript-code) idle is.

[ Voor 60% gewijzigd door Gamebuster op 25-01-2013 14:16 ]

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • TheNephilim
  • Registratie: September 2005
  • Laatst online: 11-07 12:10
Dank voor alle support, inmiddels is de bug opgelost. Mede dankzij jullie hulp is het probleem opgelost.

Misschien niet de juiste, maar makkelijkste manier was om busy = false; in de 'hashchange' callback te knikkeren.

Alsnog is het 'probleem', zoals Gamebuster al aangeeft, dat de event pas uitgevoerd word als de rest klaar is.

De oplossing van GateKeaper ga ik nog implementeren, zo hoort het natuurlijk een functie die er tussen zit om het af te handelen. Op dit moment echter geen tijd, de planning is retestrak :o
Pagina: 1