new Promise()

Native JavaScript promises!

Intro

Events

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

      img1.addEventListener('load', function() {
        // woo yey image loaded
      });

      img1.addEventListener('error', function() {
        // argh everything's broken
      });
      

Intro

Events

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

      function loaded() {
        // woo yey image loaded
      }

      if (img1.complete) {
        loaded();
      }
      else {
        img1.addEventListener('load', loaded);
      }

      img1.addEventListener('error', function() {
        // argh everything's broken
      });
      

Intro

Promises

      img1.callThisIfLoadedOrWhenLoaded(function() {
        // loaded
      }).orIfFailedCallThis(function() {
        // failed
      });

      // and…
      whenAllTheseHaveLoaded([img1, img2]).callThis(function() {
        // all loaded
      }).orIfSomeFailedCallThis(function() {
        // one or more failed
      });
      

Intro

Promises

      img1.ready().then(function() {
        // loaded
      }, function() {
        // failed
      });

      // and…
      Promise.all([img1.ready(), img2.ready()]).then(function() {
        // all loaded
      }, function() {
        // one or more failed
      });
      

Intro

Callbacks

        sum(1, 5, function (x) {
          multiply(x, 3, function (y) {
            subtract(y, 5, function (z) {
              console.log('The result is', z);
            });
          });
        });
      

Intro

Promises

        sum(1, 5)
        .then(function (x) { return multiply(x, 3); })
        .then(function (y) { return subtract(y, 5); })
        .then(function (z) { console.log('The result is', z); });
      

Intro

Promises

  • A promise can only succeed or fail once. It cannot succeed or fail twice, neither can it switch from success to failure or vice versa

  • If a promise has succeeded or failed and you later add a success/failure callback, the correct callback will be called, even though the event took place earlier

Intro

Promise states

  • fulfilled - The action relating to the promise succeeded
  • rejected - The action relating to the promise failed
  • pending - Hasn't fulfilled or rejected yet

Intro

AngularJS promises

      var deferred = $q.defer();

      // do a thing, possibly async, then…

      if (/* everything turned out fine */) {
        deferred.resolve("Stuff worked!");
      } else {
        deferred.reject(Error("It broke"));
      }

      return deferred.promise;
      

Intro

Native promises

      var promise = new Promise(function (resolve, reject) {
        // do a thing, possibly async, then…

        if (/* everything turned out fine */) {
          resolve("Stuff worked!");
        }
        else {
          reject(Error("It broke"));
        }
      });
      

Similar to rsvp.js

Instance methods

#then

      var fetchData = new Promise(function (resolve, reject) {
        // do AJAX request and then...
        resolve(response);
      });

      fetchData().then( /* handle response */ )
      

Instance methods

#then

      var fetchData = new Promise(function (resolve, reject) {
        ...
      });

      fetchData()
      .then(function (response) { return JSON.parse(response); }
      .then(function (json) { /* do something with parsed response */ })
      

Instance methods

#then

      var fetchData = new Promise(function (resolve, reject) {
        ...
      });

      fetchData()
      .then(JSON.parse)
      .then(function (json) { /* do something with parsed response */ })
      

Instance methods

#then

      var fetchData = new Promise(function (resolve, reject) {
        ...
      });

      fetchData().then(
        function (response) {
          return JSON.parse(response);
        },
        function (error) {
          /* handle error */
        }
      );
      

Instance methods

#catch

      var fetchData = new Promise(function (resolve, reject) {
        ...
      });

      fetchData()
      .then(JSON.parse)
      .catch(function (error) {
        /* handle error */
      });
      

Instance methods

#catch

      catch(someFunction);
      

is just syntactic sugar for:

      then(undefined, someFunction);
      

Error handling

#catch

      fetchData()
      .then(
        function (response) {
          console.log("Success!", response);
        },
        function (error) {
          console.log("Failed!", error);
        }
      );
      

Error handling

#catch

      fetchData()
      .then(function (response) {
        console.log("Success!", response);
      })
      .catch(function (error) {
        console.log("Failed!", error);
      });
      

Error handling

Exceptions

        var promise = new Promise(function (resolve, reject) {
          resolve(JSON.parse("This ain't JSON"));
        });

        promise.then(function (data) {
          // This never happens:
          console.log("It worked!", data);
        }).catch(function (err) {
          // Instead, this happens:
          console.log("It failed!", err);
        });
      

Error handling

Multiple catches

      get('/')
      .then( /* do something */ )
      .then( /* do something */ )
      .catch( /* handle error */ )
      .then( /* do something */ )
      .catch( /* handle error */ ));
      

Error handling

NodeJS style

        async1(function (err, res1) {
          if (err) /* handle error */

          async2(res1, function (err, res2) {
            if (err) /* handle error */

            async3(res2, function (err, res3) {
              if (err) /* handle error */

              ...
            });
          });
        });
      

Chaining

sequence

        getUsername()
        .then(function (username) {
          return getUser(username);
        })
        .then(function (user) {
          /* do something with user */
        });
      

How to access username in the second function?

Chaining

nesting

      getUsername()
      .then(function (username) {
        return getUser(username);
      })
      .then(function (user) {
        return getPassword()
        .then(function (password) {
          if (user.passwordHash !== hash(password)) {
            /* do something */
          }
        });
      });
      

Parallel

Promise.all

      Promise.all(arrayOfPromises).then(function (arrayOfResults) {
        // ...
      });
      

Parallel

Promise.all

      getJSON('story.json').then(function (story) {
        // Take an array of promises and wait on them all
        return Promise.all(
          story.chapterUrls.map(getJSON);
        );
      }).then(function (chapters) {
        chapters.forEach(function (chapter) {
          /* do something with each chapter */
        });
      });
      

Examples

Examples

jQuery $.ajax

      $.ajax({})
      .then()
      .done()
      .fail()
      .always()
      

Examples

AngularJS route.resolve

      .when "/:locale/nutrition/guides/:slug",
      resolve:
        data: (...) ->
          authenticate = () -> /* ... */
          getRules = (user) -> /* ... */
          redirectToRulesOrPass = (user, rules) -> /* ... */

          authenticate()
          .then (user) ->
            getRules(user)
            .then (rules) ->
              redirectToRuleOrPass(user, rules)
      

Examples

UI flow

      class UserSession
        constructor: (@Q, @AuthModals, @Session) ->

        signIn: ->
          @_authDeferred = @Q.defer()
          @_resolveOrPrompt()
          @_authDeferred.promise

        _resolveOrPrompt: ->
          if @_session?.signedIn
            @_completeAuth(@_session)
          else
            @AuthModals.openSignIn()
      

Examples

UI flow

@AuthModals calls completeSignIn on success

      class UserSession
        # ...

        completeSignIn: (data) ->
          @Session.save(data).then(@_completeAuth)

        _completeAuth: (session) ->
          @_session = session
          @_authDeferred.resolve(session.user)
      

Examples

UI flow

      class UserPurchases
        constructor: (@Q, @UserSession, @PurchaseModals) ->

        purchase: (@video) =>
          @UserSession.signIn().then(@_promisePurchase(video))

        _promisePurchase: (video) => (@user) =>
          @_purchaseDeferred = @Q.defer()
          @_resolveOrPrompt(video)
          @_purchaseDeferred.promise

        _resolveOrPrompt: (video) =>
          if @user.owns(video)
            @completePurchase(@user.licenseFor(video))
          else
           @PurchaseModals.openSaleForm(video)
      

Support

Chrome 32 (beta)

Firefox 29 (nightly)

Support

Links

Front Trends logo

Front Trends 2014

7th-9th May 2014 in Warsaw, Poland

Thank You!