JavaScript Promises – En sammenligning af biblioteker

I mit tidligere indlæg kiggede jeg på hvordan man kunne udføre asynkrone kald ved hjælp af promises. Nu er tiden kommet til at vælge hvilket bibliotek, der passer på det næste projekt.

Der er rigtig mange varianter og udbredelsen er stor. En søgning på promise via node-pakke-manageren npmjs.org gav 1150 biblioteker der enten tilbyder eller er afhængige af promises. Heraf har jeg valgt at kigge på 12 forskellige biblioteker, alle er open source og alle tilbyder en promise-lignende struktur.

Opdateringer:

  • 2014/03/06 – Rettet nogle stavefejl (@rauschma via Twitter)
  • 2014/03/07 – Fjernet de rå størrelser, da de ikke gav meget mening (@x-skeww via Reddit)
  • 2014/03/07 – Tilføjet at catiline underliggende bruger lie. (@CWMma via Twitter)
  • 2014/03/07 – Præciseret hvad performance-testen gør. (@CWMma via Twitter)

API’et på tværs af de mange biblioteker er næsten ens, så derfor har jeg valgt at kigge på:

Features
Hvilke overordnede promise-relaterede features indeholder hvert bibliotek?

Størrelse
Her tænker jeg mest på browseren – hvor mange ekstra bytes bliver mit website tilført?

Hastighed
Hvor hurtige er de basale promise-operationer i biblioteket? Man må forvente at disse vil blive kørt mange gange, og derfor har det en betydning.

Bibliotekerne

Først en oversigt over de udvalgte kandidater, deres licens og forfatter. Bemærk at navnet linker til bibliotekets kilde (typisk Github).

Licens Forfatter Bemærkning
Bluebird MIT Petka Antonov Fyldt med features og efter sigende en af markedets hurtigste og der er lagt speciel vægt på nem fejlsøgning vha. gode stacktraces. Features kan slåes til og fra via tilpassede builds.
Catiline MIT Calvin Metcalf Mere designet til håndtering af web workers men indeholder en promise implementation. Bruger underliggende lie.
ES6 Promise polyfill MIT Jake Archibald Låner kode fra RSVP, men implementeret så den overholder specifikationen for ECMAScript 6.
jQuery MIT  The jQuery Foundation Klassisk bibliotek til DOM-manipulation på tværs af browsere
kew Apache 2.0  The Obvious Corporation Gætter på at det skal udtales ‘Q’, kan betragtes som en optimeret udgave af Q med et mindre featuresæt
lie MIT Calvin Metcalf
MyDeferred MIT RubaXa Lille implementation i Gist format
MyPromise MIT cho45@lowreal.net Lille implementation i Gist format
Q MIT Kris Kowal Meget kendt implementation, en light-udgave af denne er at finde i det populære framework fra Google, AngularJS
RSVP MIT Tilde
when MIT cujoJS
Yui BSD Yahoo! Yahoo’s bibliotek til DOM-manipulation på tværs af browsere

Features

Nedenstående er et kig på hvilke overordnede features hvert enkelt bibliotek tilbyder. Der bliver kun kigget på de features, der er direkte relaterede til promises:

Promises/A+ Progression Delayed promise Parallel synchronization Web Workers Cancellation Generators Wrap jQuery
Bluebird ✓ (+389 B) ✓ (+615 B) ✓ (+272 B) ✓ (+396 B) ✓ (+276 B)
Catiline
ES6 Promise polyfill
JQuery
kew
lie
MyDeferred
MyPromise
Q
RSVP
when
Yui


Tallene i parantes ved Bluebird er den øgede størrelse i bytes tilvalget af den enkelte feature giver.

Promises/A+
Overholdes Promises/A+ specifikationen?

Progression
Er der funktioner til løbende notifikation af status på en aynkron opgave før at den er endelig udført?

Delayed promise
Kan man skabe en forsinket promise der bliver opfyldt efter et specificeret stykke tid?

Parallel synchronization
Er der funktioner til synkronisering af flere operationer, altså kan vi få en promise der bliver opfyldt når en række andre promises er opfyldt?

Web Workers
Kan den asynkrone kode blive afviklet via en web-worker – altså blive skubbet til en separat afviklingstråd?

Cancellation
Kan man stoppe en promise-afvikling, før den er færdig?

Generators
Understøttes de kommende funktioner omkring generators i JavaScript herunder yield?

Wrap jQuery
Kan promises produceret af jQuery blive konverteret til bibliotekets promises?

Størrelse

For de biblioteker der tilbyder tilpassede builds, er der valgt den mindste konfiguration der stadig tilbyder promises. Hvert enkelt bibliotek er blevet kørt igennem Googles Closure compiler. Alle er blevet kørt på ‘Simple’, for at undgå evt. beskadende ændringer. Det tal der kommer ud er inklusiv komprimering i http-stakken, så der er altså tale om de rå antal bytes som man må forvente at ens applikation vokser med ved tilvalg af hvert enkelt bibliotek:

Minificeret og komprimeret størrelse i KiB

Hastighed

Hastigheden er blevet målt vha. sitet jsPerf der giver mulighed for at køre de samme tests på mange forskellige browsere og platforme, inklusiv mobil og tablets. Testen skaber en ny promise med hvert enkelt framework og der bliver så målt på hvor stor forsinkelse der er i at afvikle den asynkrone del (Se flere detailjer her). Bemærk at det ikke er en test der er lavet af mig men en masse fantastiske frivillige (vi er på version 91):

Operationer-per-sekund

Konklusion

Over halvdelen af verdens websites bruger allerede jQuery. Har man arbejdet lidt med de promises jQuery tilbyder, finder man hurtigt ud af, at de er uhensigtsmæssige. Jeg har selv oplevet problemer med fejlende kode, der ikke som man forventer, afviser promisen, når der opstår fejl, men hvor fejlen alligevel bobler op og i sidste ende bliver en global browserfejl. Promise-specifikationen dikterer at fejl skal fanges og promises afvises, så her er man sikret.

Derfor, hvis man i dag har et site baseret på jQuery er det nærliggende at vælge et af de biblioteker, der tilbyder konvertering fra jQuery’s usikre promises til den mere sikre slags. Hvis størrelsen har en stor betydning, man slæber jo allerede rundt på jQuery, er enten Q eller when begge gode bud, med mange features og rimelig hastighed.

Er man ikke så bekymret for størrelsen, er Bluebird et bedre valg. Den modulære opbygning gør det nemt at vælge features til og fra og så har den en betydelig test suite der tester performance af meget andet end det ene aspekt der er dækket i det her indlæg.

Hvis performance er alt afgørende, er kew et godt bud. Det er et team der har taget Q og kigget på hvordan de kunne få forbruget af ressourcer ned – det har altså resulteret i et begrænset, men meget hurtigt bibliotek.

Går man efter en mere begrænset løsning, med god hastighed og uden store biblioteker er ES6 Promise polyfill et godt valg – dermed kan man, på sigt når browserne udvikler sig, fjerne biblioteket helt, og blot bruge de indbyggede funktioner.

This post is also available in English at Complexitymaze.com

Share Button
The following two tabs change content below.
Profile photo of Poul Foged

Poul Foged

Developer af Monzoom
Poul er medstifter af firmaet Monzoom. Han rejser verden rundt med sin bærbare under armen og så kan han godt lide at miste overblikket og dykke ned i tekniske problemer. Han blogger om softwareudvikling.
Profile photo of Poul Foged

Nyeste indlæg af Poul Foged (se alle)

Flattr this!

8 kommentarer for “JavaScript Promises – En sammenligning af biblioteker

  1. Tobias
    3. marts 2014 at 1:29 pm

    Vælger ikke at nævne noget som helst om at “wuuut, hvordan kan du vælge promises over callbacks wut wut??” 🙂

    I stedet vil jeg anbefale at du prøver http://node-modules.com til at lede efter moduler. Den bruger en ret avanceret ranking-algoritme og hvis du vil kan du signe ind med github hvor den så bruger de moduler du har starred eller programmører du følger.

    • Profile photo of Poul Foged
      3. marts 2014 at 1:40 pm

      Har arbejdet med bluebird de sidste par dage og må sige at det er ret fantastisk – det har ændret min kodestil fuldstændigt. Fejlhåndteringen alene er fantastisk. Se iøvrigt forrige indlæg.

      Callbacks er efter min mening en dårlig løsning, da jeg så skal bære dem med i alle kald m.v. fx.

      SaveUser(user, failCallback, successCallback, alwaysCallback, progressCallback);

      Hellere:

      SaveUser(user) : Promise;

      Fint site til node moduler.

      • Tobias
        3. marts 2014 at 1:46 pm

        Well, det er jo naermest en religion blandt folk, men jeg vil sige at hvis du modtager flere callbacks saa goer du ofte noget forkert. Det kan enten ske fordi din funktion/modul pludselig tager for meget ansvar eller fordi det i virkeligheden er en event-emitter.

        Jeg synes for det meste at node.js udviklere godt kan finde ud af dem paa en ordentlig maade saa det ser jeg ikke som et probem.

        Noget jeg selv gaar og bakser med er at vaenne mig til at bruge streams til alt. Det er virkelig der node spiller max, men det er noget af en tilvaenning.

  2. Profile photo of Poul Foged
    3. marts 2014 at 2:24 pm

    Ja det var overdrevet for at fremme forståelsen. Har nu svært ved at se at en løsning kan blive lige så elegant som med promises, når man også tænker på fejlhåndtering m.v.

    Jeg kan godt lide bluebirds måde at indkapsle en hel funktion i en promise, så sørger den for fejlhåndering, lange stack traces m.v. Og så er der hele synkroniseringshistorien med flere callbacks – det bliver bare imo. mere elegant med promises.

    Events har da også deres rolle, men giver tit mere arbejde med livscyklus m.v.

    Ja, reaction streams er for fede, og også lidt svære at venne sig til – det er sådan lidt en process hvor man ender med at kunne fjerne en masse kode 🙂

  3. Lars
    4. marts 2014 at 8:37 am

    Sådan af interesse, hvorfor ville man vælge callbacks frem for at bruge promises ?

    Fra mit synspunkt, så er promises med til at gøre det hele mere læsbart og vedligeholdelses venligt.

    Jeg har lavet applikationer i angular det sidste års tid, og i har i den forbindelse brugt angulars promise bibliotek q. Det er jeg meget begejstret for 🙂
    På trods af at q er en lidt minimeret udgave af Q.

    • Profile photo of Poul Foged
      4. marts 2014 at 8:47 am

      Godt spørgsmål.

      Sådan lidt tænkt, så kan man næsten få en GOTO-agtig opførsel med en lang callback-stak. Hvor man med promises mere får noget der ligner exception-handling i callback forløbet.

      • Lars
        5. marts 2014 at 6:49 am

        Ja, det får mig nu ikke til at vælge callback’s frem for promises.

        Det var egentlig også et spm til @Tobias’s første svar, det lader til han vil ?

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *