Javascript Promises

Agenda

  • Callback Review
  • Using a Promise
  • Making a Promise
  • Interesting Uses
  • Quiz
db.query("SELECT * FROM students", function(err, students) {
  if(err) {
    logger.error(err);
    return;
  }

  //do stuff with students
});
db.query("SELECT * FROM students", function(err, students) {
  if(err) {
    logger.error(err);
    return;
  }

  db.query("SELECT * FROM teachers", function(err, teachers) {
    if(err) {
      logger.error(err);
      return;
    }

    //do stuff with students and teachers
  });
});
var students, teachers;

db.query("SELECT * FROM students", function(err, _students) {
  if(err) {
    logger.error(err);
    return;
  }

  students = _students;

  if(students && teachers) {
    //do stuff with students and teachers
  }
});


db.query("SELECT * FROM teachers", function(err, _teachers) 
  if(err) {
    logger.error(err);
    return;
  }

  teachers = _teachers;

  if(students && teachers) {
    //do stuff with students and teachers
  }
});

Callbacks Don't Compose

  • Circa 2012
  • Unified API
  • Holds a single value
    • .then()
  • In a state of
    • Pending
    • Resolved
    • Rejected

What is a Promise?

  • ES6
  • Native in:
    • Node
    • Chrome
    • FF
    • Safari
    • Edge

Where I can use them?

  • .then(onResolve, [onReject])
  • .catch(onReject)

Using a Promise

Using a Promise


promise
  .then((value) => {
     //do something with value
  })
  .catch((err) => {
     //handle an error
  });

Using a Promise


promise
  .then((value) => {
     //do something with value
  }, (err) => {
     //handle an error
  });

Errors and Rejections


promise
  .then((value) => {
     return 1;
  })
  .catch((err) => {
     // won't be called
  });

Errors and Rejections


promise
  .then((value) => {
     return Error("WAT");
  })
  .catch((err) => {
     // won't be called
  });

Errors and Rejections


promise
  .then((value) => {
     throw Error("WAT");
  })
  .catch((err) => {
     // WILL be called
  });

Chaining


promise
  .then((value) => {
     //do something with value
  })
  .then((value) => {
     //do something else
  })
  .catch((err) => {
     //handle an error
  })
  .then(() => {
     // do something later
     // even after an error
  });

Composing


promise
  .then((value) => {
     return anotherPromise
       .then(() => {
         console.log("This will run first.");
       });
  })
  .then(() => {
    console.log("This will run second.");  
  })
  .catch((err) => {
     // handle an error in
     // any of the promises
  });

Composing


Promise.all([
  promise1,
  promise2,
  promise3
])
  .then((values) => {
     // do something with all three
  })
  .catch((err) => {
     // handle an error in
     // any of the promises
  });

Composing


var ids      = [1, 2, 3, 4, 5, 6];
var promises = ids.map((id) => fetch(id));

Promise.all(promises)
  .then((values) => {
     // do something with all six
  })
  .catch((err) => {
     // handle an error in
     // any of the promises
  });

Creating Promises

Promise.resolve(17);

Promise.reject(Error("NOPE"));

new Promise((resolve, reject) => {
});

Creating Promises

db.query = function(sql, callback) {
  // 3rd party code that we don't control
}

db.promiseQuery = function(sql) {
  return new Promise((resolve), reject) => {
    db.query(sql, function(err, results) {
      if(err) {
        reject(err);
      }
      else {
        resolve(results);
      }
    });
  });
}
var students, teachers;

db.query("SELECT * FROM students", function(err, _students) {
  if(err) {
    logger.error(err);
    return;
  }

  students = _students;

  if(students && teachers) {
    //do stuff with students and teachers
  }
});


db.query("SELECT * FROM teachers", function(err, _teachers) 
  if(err) {
    logger.error(err);
    return;
  }

  teachers = _teachers;

  if(students && teachers) {
    //do stuff with students and teachers
  }
});
Promise.all([
  db.promiseQuery("SELECT * FROM students"),
  db.promiseQuery("SELECT * from teachers")
]).then((results) => {
  var students = results[0];
  var teacher  = results[1];
 
  //do stuff with students and teachers
}).catch((err) => {
  logger.error(err);
});
Promise.all(promises)
Promise.race(promises)

Promise.resolve(resolvedValue);
Promise.reject(rejectedValue);

new Promise((resolve, reject) => {
})

.then(onResolve, [onReject])
.catch(onReject)

Promises API

Where are Promises used in LT?

  • Everywhere! ;)
  • All Redis queries
  • All HTTP Requests
  • Run to completion, or
  • time out after 30 seconds

LT Load Test

var timeoutPromise = function(delay) {
  return new Promise((resolve, reject) => {
    setTimeout(reject, delay);
  });
}

Promise.race([
  loadTestCompletionPromise,
  timeoutPromise(30 * 1000)
]).then(() => {
  // load test succeeded! :)
}).catch(() => {
  // load test timed out! :(
});

LT Load Test

Chunking

  • 100,000 promises
  • Promise.all() would crush the system 
var ids = [1, 2, 3, ..., 100000];

var chunks = _.chunk(ids, 5000);

sessionStore.findAll(chunks[0])
  .then((sessions) => {
    //do stuff
  })
  .then(() => {
    return sessionStore.findAll(chunks[1]);
  })
  .then((sessions) => {
    //do more stuff
  })
  .then(() => {
    return sessionStore.findAll(chunks[2]);
  })

//etc.

Chunking

var ids = [1, 2, 3, ..., 100000];

var chunks = _.chunk(ids, 5000);

var promiseGenerators = chunks.map((chunk) => {
  function() {
    return sessionStore.findAll(chunk);
  }
});

promiseGenerators.reduce((memo, f) => {
  memo.then(f);
}, Promise.resolve([]));

Chunking

Quiz Time!

function doStuff() {
  return Promise.resolve(17);
}

doStuff()
  .then(() => {
    console.log('first');
  })
  .then(() => {
    console.log('second')
  })
  .catch((err) => {
    console.log('REJECTED!')
  });
function doStuff() {
  return Promise.reject(17);
}

doStuff()
  .then(() => {
    console.log('first');
  })
  .then(() => {
    console.log('second')
  })
  .catch((err) => {
    console.log('REJECTED!')
  });
function doStuff() {
  return Promise.reject(17);
}

doStuff()
  .catch((err) => {
    console.log('REJECTED!')
  })
  .then(() => {
    console.log("AFTER REJECTION!");
  })
  .catch(() => {
    console.log("I'M STILL REJECTED!");
  });
function doStuff() {
  return Promise.resolve(17);
}

function sayHello(name) {
  return Promise.resolve("Hi, " + name);
}

doStuff()
  .then((value) => {
    console.log("Value is: " + value);
    
    return Promise.all([
      sayHello("Joel"),
      sayHello("Manda")
    ]);
  })
  .then((greetings) => {
    greetings.forEach(g => console.log(g));
  });
function doStuff() {
  return Promise.resolve(17);
}

var p = doStuff();

p.then(() => Promise.reject(Error("NOPE")))

p.then((value) => {
    console.log("SUCCESS!");
  })
  .catch((err) => {
    console.log("ERROR!");
  });

Promise.resolve(this.talk);

promises

By Chris Geihsler

promises

  • 1,233