JavaScript Callback of Promise

Als je nu https://www.facebook.com opent, zie je dat de pagina meteen laadt. Je eigen foto linksboven is zichtbaar, de zoekbalk bovenaan het scherm en het menu aan de linkerkant. In de seconden daarna verschijnen langzaam de berichten op de tijdlijn, niet allemaal, maar alleen in het gedeelte van het scherm dat zichtbaar is. Als je nu naar beneden scrolt, zie je dat ook de oudere berichten pas op dat moment geladen worden.

Facebook laadt deze onderdelen na elkaar om er voor te zorgen dat de pagina snel zichtbaar is. Als Facebook zou wachten met het tonen van de pagina totdat alle gegevens en berichten geladen zijn, zou het zeker een aantal seconden duren voordat de Facebook pagina zichtbaar is. En volgens studies is het zo dat hoe langer het duurt dat een website laadt, hoe meer gebruikers afhaken.

Asynchroon programmeren

Met asynchroon programmeren voorkom je dat de code moet wachten op uitvoer voordat het verder kan. Bij Asynchroon programmeren vindt de communicatie binnen de code plaats met behulp van events en callbacks. Een callback wordt aangeroepen als de data beschikbaar is. Vanaf dat punt kan het programma weer verder.

Op het moment dat er asynchrone code aanwezig is, heeft de ontwikkelaar de keuze tussen het gebruik van Callbacks of Promises. Een Promise is een nieuwer concept. Het voegt een extra laag van abstractie toe aan de code.

In principe is er niets mis met een Callback, maar het wordt lastiger wanneer je te maken krijgt met geneste callbacks. Geneste callbacks ontstaan als er meerdere dingen achter elkaar uitgevoerd moeten worden en waarbij de nieuwe aanroep moet wachten op de vorige aanroep. In de code ontstaat dan de “Pyramid of Doom”. Dit maakt de code moeilijk leesbaar en om die reden kan je dit beter voorkomen.

pyramidOfDoom

Callbacks

Als voorbeeld het laden van een afbeelding. Het resultaat van het load proces vangen we af door callbacks te registreren:

var img1 = document.querySelector('.img-1');

img1.addEventListener('load', function() {
  // Image loaded.
});

img1.addEventListener('error', function() {
  // Image loading failed.
});

In het voorbeeld hierboven, kan het zo zijn dat ‘load’ event al afgevuurd is voordat de we begonnen met luisteren naar het ‘load’ event. In dat geval ontvangen we nooit de melding dat de afbeelding geladen is.

Dit probleem is op te lossen door extra code toe te voegen die kijkt naar de status van img.complete, maar dat maakt de code er niet duidelijker op. Om toch elegante code op te leveren, kunnen we gebruik maken van een Promise object.

Promise

In de praktijk

Een Promise is het object wat je terug krijgt uit een function call wanneer de waarde die de functie moet teruggeven niet direct beschikbaar is, maar je er niet op wilt wachten. Je krijgt dan een Promise object terug waar je een eventhandler aan kunt hangen welke uitgevoerd wordt als de functie klaar is.

Een Promise object kan slechts eenmaal van status wijzigen. Als de Promise de status “resolved” eenmaal heeft, kan deze status niet meer wijzigen.

Eenvoudig voorbeeld

Een voorbeeld van een Promise object in Angular. Een enkele call met success en error afhandeling.

DsrContact.query({relationId: controller.relationId}).$promise
    .then(function (contacts) {
        controller.contacts = contacts;
        
        // Etc.
    })
    .catch(function (response) {
        // Handle error.
    });

Chaining voorbeeld

Een voorbeeld van meerdere calls in serie uitgevoerd. Er is nog steeds maar één catch functie voor de error afhandeling.

DsrContact.query({relationId: controller.relationId}).$promise
    .then(function (contacts) {
        // Todo: Process contacts.
        return contacts;
    })
    .then(function (processedContacts) {
        controller.contacts = processedContacts;
        // Etc.
    })
    .catch(function (response) {
        // Handle error.
    });

Voorbeeld parallelle requesten

In dit laatste voorbeeld worden twee calls gelijktijdig uitgevoerd. Met de $q.all() functie wordt daarna gewacht totdat alle calls succesvol uitgevoerd zijn. Pas daarna wordt de $q.then() functie aangeroepen.

var sitePromise = DsrSite.query({relationId: controller.relationId}).$promise;
var assetPromise = DsrAsset.query({relationId: controller.relationId}).$promise;

// Execute REST calls in parallel.
$q
    .all([sitePromise, assetPromise])
    .then(function (values) {
        // All REST calls successfully.
        controller.sites = values[1];
        controller.assets = values[2];

        // Etc.
    })
    .catch(function (response) {
        // Handle error.
    });

Browser Support

Native Promises worden bij het schrijven van dit artikel inmiddels door alle browsers behalve Internet Explorer, ondersteund. Daarnaast bestaan er libraries als Q die Promises volgens de specificaties implementeren en ook te gebruiken zijn in oudere browsers.

Conclusie

Promises zijn een krachtige aanvulling die het schrijven van asynchrone code makkelijker en overzichtelijker maakt. Daarnaast zijn composities en error afhandeling eleganter te programmeren, de code blijft veel compacter, bevat minder indents en is daardoor veel beter leesbaar dan geneste callback functies.

Resources

Hack de ATAG One Android App

PlayStore-AtagOneApp
De ATAG One app, gemaakt door Applied Micro Electronics AME B.V.

De ATAG One app (voor iPhone en Android) benadert rechtstreeks via het locale netwerk de ATAG One thermostaat. Atag stelt helaas geen technische documentatie online beschikbaar hoe de thermostaat aan te sturen is. De enige manier om uit te vissen hoe de app “praat” met de thermostaat, is door de app te hacken 🙂

Lees verder…

AngularJS; Lege velden na reload, opgelost!

Verschrikkelijk irritant, dat na een reload van de pagina alle velden van de webapplicatie weer op de initiële waarde staan. Het is veel praktischer als ze nog steeds dezelfde waarde bevatten na de reload. Hieronder beschrijf ik hoe je dit voorelkaar krijgt in het webframework AngularJS.

Lees verder…

Fast JavaScript Click Event for Touch Devices

Click Events

When working on a web app for touch devices (iPhone / iPad), you will soon notice that click events are rather slow to fire. There is a delay due to the fact that the device waits for the user to complete a gesture before deciding that the intended gesture was in fact a click.

I wrote a simple script that detects whether the device is touched without moving. When tapped without move, it fires a custom event named “fast click”. This alternative event fires much faster then an ordinary click event.

Continue reading “Fast JavaScript Click Event for Touch Devices”

HTML 5 Accelerometer

Wat is een Accelerometer

Volgens wikipedia is een accelerometer (versnellingsmeter in het Nederlands):

Een versnellingsmeter is een meetapparaat dat een versnelling kan registreren en meten. Het maakt gebruikt van het traagheidsprincipe.

Tegenwoordig is bijna elke smartphone uitgerust met een accelerometer. Met de accelerometer is het mogelijk de rotatie digitaal uit te lezen.

Lees verder…

Mockup m.b.v. jQuery Mobile

screenshot-jquery-mobile

Wat is jQuery Mobile

De makers van jQuery werken sinds een paar maanden aan jQuery Mobile.

“It’s a framework built on top of jQuery that provides a range of user interface elements and features for you to use in your mobile apps. jQuery Mobile uses the very best HTML 5 and CSS 3 features to provide the best possible experience in the most-capable browsers.”

Afgelopen jaar, op 12 november 2010 hebben de mannen van jQuery Mobile een tweede alfa versie uitgebracht – jQuery Mobile Alpha 2.

Lees verder…

Multiselect jQuery component

HTML Selectbox component

Een standaard HTML Selectbox component geeft de gebruiker de mogelijkheid om meerdere opties tegelijk te selecteren. Echter de manier waarop dit moet gebeuren, is niet erg gebruikersvriendelijk en meestal ook onbekend bij de “gewone” gebruikers.

Bij een lijst van checkboxes daarentegen is het meteen duidelijk dat er meerdere opties aangevinkt kunnen worden. Een “gewone” gebruiker is al bekend met een checkbox.

Met jQuery is het vrij eenvoudig om een standaard multiselectbox te transformeren naar deze andere custom variant hierboven besproken.

Lees verder…

Multiselect transfer component

Multiselect transfer component mbv jQuery

Een standaard HTML selectbox geeft de gebruiker de mogelijkheid om meerdere opties tegelijk te selecteren. De manier waarop dit gebeurt, is niet erg gebruikersvriendelijk, maar belangrijker, meestal onbekend bij de “gewone” gebruikers.

Veel bekender is een component om opties tussen twee panels van links naar rechts te verplaatsen. Links staat de lijst met alle mogelijke opties waaruit een gebruiker kan kiezen en rechts de lijst met de geselecteerde opties.

Met jQuery is het vrij eenvoudig om een standaard multi-selectbox component te transformeren naar deze andere variant hierboven besproken.

Lees verder…