Skip to content

Instantly share code, notes, and snippets.

@zcaceres
Created March 15, 2017 17:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zcaceres/add95fca6f5c192593dbc135cc95286b to your computer and use it in GitHub Desktop.
Save zcaceres/add95fca6f5c192593dbc135cc95286b to your computer and use it in GitHub Desktop.
understanding the behavior of promises under multiple conditions – FSA 1702 - March 15, 2017

Mechanics of Promises

Understanding Javascript Promise Generation & Behavior

1. Good Old Promises

2. The Magic of Promises


1. Good Old Promises

Explain the behavior of promises under multiple conditions. Build my own promise library

In the past, we did regular async. e.g.

client.query(query, callback) {
  if (err) return next(err);
  res.json(data.rows)
}

Sometimes this is called the continuation-passing model

When we use Page.findOne in Sequelize, we're doing this same process.

We don't have Promises just to avoid callback hell!

In Promises we can:

  • separate the async request from the eventual behavior that we want to run.
  • pass the Promise around to other modules (portable)
  • Multiple handlers in linear/flat chains (not callback hell)
  • collect Promises into an array and pass them into a function
  • Unified error handling (.catch), rather than in every callback

What is a Promise?

"A promise represents the eventual result of an asynchronous operation."

Three states of promises:

  • Pending (contains nothing)
  • Fulfilled (contains value from promise)
  • Rejected (contains error)

Promises are Objects

Promises are POJO! Plain Old Javascript Objects :-)

  state(pending, fulfilled, or rejected)  // hidden if possible
    information(value or a reason)        // hidden if possible
        .then() // <== a public property!

Remember, promises only change state while pending. If a promise is fulfilled or rejected, it can't go back to pending.

Standards

Promises/A+ is the standard that won. ES6 are a superset of this standard.

Don't forget, Promises are not a fundamental type like a string or a boolean. They're implemented.

    // Fake solution to Promise implementation
    const containerA = new Container();

    asyncGetData(function (data) {
      containerA.save( data ); // once async completes
    });

    // later...
    containA.whenSaved( function ( data ) {
      console.log( data ); // once containerA.save() happens
    }

We are just putting a value into a box!

Making Promises

So how do we make new Promises?

new Promise(executor)
    const promiseforTxt = new Promise(function (resolve, reject) {
      // Executor function takes a func with resolve and reject
      fs.readFile('path.txt', function (err,text) {
        if (err) reject( err ); // if we reject, we put an error in our Promise box (Rejected with error)
        else resolve( text ); // if we resolve, we put a value into the box! (Fulfilled with value)
      });
    };

    promiseForTxt.then( someSuccessHandler, someErrorHandler );

2. The Magic of Promises

1. It doesn't matter when .then() is called.

WHAT?! We can call it before or after the promise has resolved. This is like 'deciding' what you'll do with the value of the promise before it has resolved.

Calling .then on same promise

A single promise can have .then called on it at various points in your code, including before the promise was resolved.

When that promise is resolved, all the .thens are resolved.

  var promiseOne = promisifiedReadFile('text-one.txt');

  promiseOne.then(function(val) {
    console.log(val, 'logged from first .then()');
  });
  // While promise is processing, we have added a .then() to it

  promiseOne.then(function(val) {
    console.log(val, 'logged from second .then()');
  });
  // While promise is processing, we added ANOTHER .then() to it

  /*  We will see our first log then our second log because the `.then`s will be resolved in the order that we added them.

  .then() is grabbing the value from the promise */
  var promiseOne = promisifiedReadFile('text-one.txt');

  promiseOne.then(function(val) {
    console.log(val, 'logged from first .then()');
  });
  // While promise is processing, we have added a .then() to it

  promiseOne.then(function(val) {
    console.log(val, 'logged from second .then()');
  });

  setTimeout(function() {
    promiseOne.then(green)}, 3000
  });
  /* Crazy! Will log our text file in green after a three second delay.  */

2. .then() returns a new, different Promise

This is why we can chain .then().

So what happens if we return in a .then...

  const promiseB = promiseA.then(function thingSuccess(thing) {
    return thingB;
  })

If there is no handler... If there is no success handler, your .then looks for the first available success handler. If there is no failure handler, your .then looks for the first available failure handler.

Success and failure bubbles/trickles down through the .then chain until it finds something to handle it.

If there is a handler...

If we reach a success handler with a value from a resolved Promise, we resolve the next promise with the value (result) from before.

If we return a totally new promise, then the next Promise that comes in that chain IS that promise.

  promisifiedReadFile('text-one.txt')
  .then() // No success handler, keep going.
  .then(function(value) {
    return 'I am the new value now!'; // We replace our old value with the returned one here.
  }).then(function(value) {
    console.log(value); // Logs 'I am the new value now!'
  });
  promisifiedReadFile('text-one.txt')
  .then(function(value) {
    console.log(value); // Logs my text file
    // if I want the logging to continue I would have to return value here
  }).then(function(value) {
    console.log(value); // Logs nothing, because the value is undefined
  }).then(function(value) {
    console.log(value); // Logs nothing, because the value is undefined
  }).then(function(value) {
    console.log(value); // Logs nothing, because the value is undefined
  }).then(function(value) {
    console.log(value); // Logs nothing, because the value is undefined
  })
  promisifiedReadFile('text-one.txt')
  .then(function(value) {
    console.log(value);
    return promisifiedReadFile('text-two.txt'); // We return a new promise with the text of our second file
  }).then(function(value) {
    console.log(value); // Logs our second text file
  });

** If the promise rejects with a reason ** The value for error will be the error we threw in our last function.

This will look for an error handler in our .then chain. Once the error is dealt with, the .then chain will return to the success track.

  promisifiedReadFile('this-file-doesnt-exist.txt')
  .then(function(value) {
    console.log(value);
    return promisifiedReadFile('text-two.txt'); // No error handler!
  }).then(function(value) {
    console.log(value); // No error handler!
  }).then(function(value) {
    console.log(value); // No error handler!
  }).then(function(value) {
    console.log(value); // No error handler!
  }).then(null, function(err) {
    console.error(err); // Logs our error!
  });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment