$30 off During Our Annual Pro Sale. View Details »

Promises, Promises

Promises, Promises

Presentation from Meet.js Warsaw + Adobe https://www.facebook.com/events/272800652850131/
(or http://www.adobe.com/pl/event/create-now-world-tour.html)

Basic concepts about Promises (mostly with Q.js)

krzychukula

March 01, 2013
Tweet

More Decks by krzychukula

Other Decks in Programming

Transcript

  1. Promises, Promises
    @krzychukula
    krzychukula.blogspot.com

    View Slide

  2. View Slide

  3. Beware
    ● Code examples inspired by: Q.js
    ● Mixing polish and english
    ● May be complately wrong
    ● 20min is not enough!

    View Slide

  4. View Slide

  5. The Pyramid of Doom

    View Slide

  6. How async code looks like?

    View Slide

  7. function onInitFs(fs) {
    fs.root.getFile('foo.txt', {}, function(fileEntry) {
    }, errorHandler);
    }

    View Slide

  8. function onInitFs(fs) {
    fs.root.getFile('foo.txt', {}, function(fileEntry) {
    fileEntry.file(function(file) {
    }, errorHandler);
    }, errorHandler);
    }

    View Slide

  9. function onInitFs(fs) {
    fs.root.getFile('foo.txt', {}, function(fileEntry) {
    fileEntry.file(function(file) {
    var reader = new FileReader();
    reader.onloadend = function(e) {
    var txtArea = document.createElement('textarea');
    txtArea.value = this.result;
    document.body.appendChild(txtArea);
    };
    reader.readAsText(file);
    }, errorHandler);
    }, errorHandler);
    }

    View Slide

  10. function onInitFs(fs) {
    fs.root.getFile('foo.txt', {}, function(fileEntry) {
    fileEntry.file(function(file) {
    var reader = new FileReader();
    reader.onloadend = function(e) {
    function onInitFs(fs) {
    fs.root.getFile('foo.txt', {}, function(fileEntry) {
    fileEntry.file(function(file) {
    var reader = new FileReader();
    reader.onloadend = function(e) {
    var txtArea = document.createElement('textarea');
    txtArea.value = this.result;
    document.body.appendChild(txtArea);
    };
    reader.readAsText(file);
    }, errorHandler);
    }, errorHandler);
    };
    reader.readAsText(file);
    }, errorHandler);
    }, errorHandler);
    }

    View Slide

  11. JS without Promises

    View Slide

  12. Nicer abstraction on File API
    readFoo(success, error)

    View Slide

  13. Write test to check read/write
    writeFoo(function(){
    readFoo(function(text){
    expect(text).toBe('foo');
    }, error)
    }, error)

    View Slide

  14. writeFoo(function(){
    readFoo(function(text){
    expect(text).toBe('foo');
    }, error)
    }, error)

    View Slide

  15. Start again with promises
    writeFoo()
    //==> returns promise
    writeFoo().done(success, error)

    View Slide

  16. Not enough
    writeFoo().done(function(){
    readFoo().done(function(text){
    expect(text).toBe('foo');
    }, error)
    }, error)

    View Slide

  17. View Slide

  18. Promises

    View Slide

  19. Let's learn it!
    var promise = writeFoo()

    View Slide

  20. Promise what it is?

    View Slide

  21. Object
    Promise

    View Slide

  22. With State
    Promise state

    View Slide

  23. Subscribe to state changes
    Promise
    then
    done
    fail

    View Slide

  24. State
    Promise
    pending
    resolved
    rejected
    state

    View Slide

  25. Subscribe to state
    done(success, error)
    resolved rejected

    View Slide

  26. Just like done*
    then(success, error)
    resolved rejected

    View Slide

  27. Only fail handler
    fail(error)
    resolved rejected

    View Slide

  28. See it in practice
    var promise = writeFoo();
    promise.done(success, error)

    View Slide

  29. Many listeners

    View Slide

  30. You can attach listeners to it
    var promise = writeFoo()
    promise.done(success, error)

    View Slide

  31. Many listeners for one promise!
    var promise = writeFoo()
    promise.done(log, logError)
    promise.done(updateStats, error)
    promise.done(displayInfo, displayError)

    View Slide

  32. Callback
    writeFoo(funciton(data){
    log(data);
    updateStats(data);
    displayInfo(data);
    }, error)

    View Slide

  33. Many listeners for one promise!
    function writeFooAndLog(){
    var promise = writeFoo()
    promise.done(log, error)
    return promise;
    }
    var promise = writeFooAndLog();
    promise.done(successDialog, errorDialog)

    View Slide

  34. Promise lifetime

    View Slide

  35. Attach listeners even when it's done!
    var promise = readFoo()
    promise.done(log, error)
    ==> resolves with cache...

    View Slide

  36. You know, order matters!
    var img = new Image();
    img.src = "sprite.png";
    img.onload = function(){
    //never called
    }

    View Slide

  37. Promise is persistent

    View Slide

  38. What if...
    var img = new Image();
    var promise = img.load("sprite.png");
    promise.done(function(){ })

    View Slide

  39. Attach listeners even when it's done!
    var promise = readFoo()
    promise.done(log, error)
    ==> resolves with cache...
    promise.done(updateStats, error)
    promise.done(displayInfo, error)

    View Slide

  40. The same is true for fails
    var promise = writeFoo()
    promise.done(log)
    promise.fail(revertTransaction)
    promise.fail(logError)

    View Slide

  41. Before we go deeper
    Promise can change state only once.

    View Slide

  42. Chains

    View Slide

  43. jQuery API example
    $('widget')

    View Slide

  44. $('widget')
    .addClass('important')

    View Slide

  45. $('widget')
    .addClass('important')
    .show()

    View Slide

  46. $('widget')
    .addClass('important')
    .show()
    .find('h1')

    View Slide

  47. Chaining
    $('widget')
    .addClass('important')
    .show()
    .find('h1')
    .text('Special Offers')

    View Slide

  48. Then

    View Slide

  49. Similar to done
    .done(asyncSuccess, error)
    .then(asyncSuccess, error)

    View Slide

  50. Difference
    .done(asyncSuccess, error)
    //==> undefined
    .then(asyncSuccess, error)
    //==> promise

    View Slide

  51. Create chains for async
    promise
    .then(asyncSuccess, error)
    .then(syncSuccess2, error2)

    View Slide

  52. Then always returns new promise
    promise
    .then(success, error)
    .then(success2, error2)
    //==> promise

    View Slide

  53. Then and errors!
    promise
    .then(success , error)
    .then(success2 , error2)
    .then(success3 , error3)
    .then(success4, error4);
    * not in jQuery

    View Slide

  54. Then and errors!
    promise
    .then(succes)
    .then(success2)
    .then(success3)
    .then(success4, fail);

    View Slide

  55. So 'then' is a better 'done'
    then > done
    * at least I thinked so...

    View Slide

  56. Let's fix this test example

    View Slide

  57. The one with pyramid
    writeFoo().done(function(){
    readFoo().done(function(text){
    expect(text).toBe('foo');
    }, error)
    }, error)

    View Slide

  58. But start from scratch.
    writeFoo()

    View Slide

  59. writeFoo()
    .then(readFoo)

    View Slide

  60. writeFoo()
    .then(readFoo)
    .then(function(text){
    //==> content of foo file
    }, error);

    View Slide

  61. Final Test Example*
    writeFoo()
    .then(readFoo)
    .then(function(text){
    expect(text).toBe('foo');
    }, fail);

    View Slide

  62. Troubles

    View Slide

  63. When I was starting
    I've put promises everywhere!
    I've used then - because it's better

    View Slide

  64. I got some
    Broken scripts
    No errors in console
    WAT!
    Time for debugger...

    View Slide

  65. Reason: Stupid typo
    then(showLenght)
    then(showLength)

    View Slide

  66. View Slide

  67. but wait...
    when in trouble
    Promises considered harmful!

    View Slide

  68. View Slide

  69. Q.js Readme
    return foo()
    .then(
    function (value) {
    throw new Error("Can't bar.");
    },
    function (error) {
    // We only get here if "foo" fails
    }
    );

    View Slide

  70. Error handler AFTER callback
    return foo()
    .then(function (value) {
    throw new Error("Can't bar.");
    })
    .fail(function (error) {
    // We get here with any callback error
    });

    View Slide

  71. Why?
    .then()
    //=> promise
    1: Then always returns promise

    View Slide

  72. Why?
    .then()
    //=> promise
    1: Then always returns promise
    2: Promise catches all errors

    View Slide

  73. Why?
    .then()
    //=> promise
    1: Then always returns promise
    2: Promise catches all errors
    3: And passes them to the last promise

    View Slide

  74. fail fast
    writeFoo()
    .then(readFoo)
    .then(function(text){
    expect(text).toBe('foo');
    }, fail)
    .done();
    //==>undefined

    View Slide

  75. So we can fix test example
    writeFoo()
    .then(readFoo)
    .then(function(text){
    expect(text).toBe('foo');
    }, fail);
    //==> promise

    View Slide

  76. Done! :)
    writeFoo()
    .then(readFoo)
    .done(function(text){
    expect(text).toBe('foo');
    }, fail);
    //==>undefined

    View Slide

  77. Deferred

    View Slide

  78. Deferred
    How can I change state of promise?

    View Slide

  79. Deferred Promise

    View Slide

  80. Deferred Promise
    READONLY
    Master of
    promise

    View Slide

  81. Deferred Promise
    READONLY
    resolve reject

    View Slide

  82. Show me the code
    function getFoo(){
    var deferred = Q.defer();
    FS.readFile("foo.txt", function (error, text) {
    if (error) {
    deferred.reject(new Error(error));
    } else {
    deferred.resolve(text);
    }
    });
    return deferred.promise;
    }

    View Slide

  83. Create Deferred
    function getFoo(){
    var deferred = Q.defer();
    FS.readFile("foo.txt", function (error, text) {
    if (error) {
    deferred.reject(new Error(error));
    } else {
    deferred.resolve(text);
    }
    });
    return deferred.promise;
    }

    View Slide

  84. Return promise from deferred
    function getFoo(){
    var deferred = Q.defer();
    FS.readFile("foo.txt", function (error, text) {
    if (error) {
    deferred.reject(new Error(error));
    } else {
    deferred.resolve(text);
    }
    });
    return deferred.promise;
    }

    View Slide

  85. success == resolve
    function getFoo(){
    var deferred = Q.defer();
    FS.readFile("foo.txt", function (error, text) {
    if (error) {
    deferred.reject(new Error(error));
    } else {
    deferred.resolve(text);
    }
    });
    return deferred.promise;
    }

    View Slide

  86. error == reject
    function getFoo(){
    var deferred = Q.defer();
    FS.readFile("foo.txt", function (error, text) {
    if (error) {
    deferred.reject(new Error(error));
    } else {
    deferred.resolve(text);
    }
    });
    return deferred.promise;
    }

    View Slide

  87. Wrap it in function
    function getFoo(){
    var deferred = Q.defer();
    FS.readFile("foo.txt", function (error, text) {
    if (error) {
    deferred.reject(new Error(error));
    } else {
    deferred.resolve(text);
    }
    });
    return deferred.promise;
    }

    View Slide

  88. getFoo()
    .then(joinLines)
    .then(displayContent, showError)
    .done();

    View Slide

  89. All

    View Slide

  90. 3 simultaneous calls
    3 ajax calls at once

    View Slide

  91. Without pyramid

    View Slide

  92. without call counting
    var ajaxCalls = 3;
    success: function(){
    if(--ajaxCalls == 0) letUserKnow

    View Slide

  93. var promise1 = $.ajax(one);
    var promise2 = $.ajax(two);
    var promise3 = $.ajax(three);
    Q.all([promise1, promise2, promise3])
    .done(letUserKnow);

    View Slide

  94. var promise1 = $.ajax();
    var promise2 = $.ajax();
    var promise3 = $.ajax();
    Q.all([promise1, promise2, promise3])
    .done(letUserKnow)

    View Slide

  95. var promise1 = $.ajax();
    var promise2 = $.ajax();
    var promise3 = $.ajax();
    Q.all([promise1, promise2, promise3])
    .done(letUserKnow);

    View Slide

  96. var promise1 = $.ajax();
    var promise2 = $.ajax();
    var promise3 = $.ajax();
    Q.all([promise1, promise2, promise3])
    .done(letUserKnow);

    View Slide

  97. Summary

    View Slide

  98. InitFs()
    .then(getFile('foo.txt', {}))
    .then(openFile)
    .then(readAsText)
    .done(displayInTextarea, errorHandler)

    View Slide

  99. Many listeners for one promise!
    var promise = writeFoo()
    promise.done(log)
    promise.done(updateStats)
    promise.fail(logError)
    promise.fail(displayError)

    View Slide

  100. You can chain promises with 'then'
    promise
    .then(succes)
    .then(success2)
    .then(success3)
    .then(success4, fail);

    View Slide

  101. When You See No Errors
    .done()

    View Slide

  102. Only 2 slides till the end ;)
    Thanks for listening!

    View Slide

  103. Instead of QA
    Async JavaScript by Trevor Burnham
    github.com/medikoo/deferred
    www.medikoo.com/asynchronous-javascript/ by
    Mariusz Nowak
    github.com/kriskowal/q

    View Slide

  104. The end
    Let me know how I can be better:
    @krzychukula

    View Slide