Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Sweet Patterns for Promises

Sweet Patterns for Promises

A collection of Promises/A+ usage patterns

Thanos Polychronakis

January 29, 2015
Tweet

More Decks by Thanos Polychronakis

Other Decks in Technology

Transcript

  1. Single Callback Guarantee Single Callback Guarantee entity.create() .then(function(udo) { //

    once for either outcome }) .catch(function(error) { // once for either outcome });
  2. Robust Error Handling Robust Error Handling entity.create() .then(function(udo) { //

    any error that happens in here // will get caught in ".catch()" throw new Error('ops'); }) .catch(function(error) {});
  3. Creating a Novel Promise Creating a Novel Promise Typically interfacing

    with vanilla callbacks Typically interfacing with vanilla callbacks var Promise = require('bluebird'); /* ... */ ClipEntity.prototype.fetchVideos = function() { return new Promise(function(resolve, reject) { fs.read(file, function(err, res) { if (err) { reject(err); } else { resolve(res); } }); }); };
  4. Interfacing with Promises Interfacing with Promises When your interfaces are

    all returning promises When your interfaces are all returning promises var Promise = require('bluebird'); /* ... */ ClipEntity.prototype.showVideos = Promise.method(function() { return this.fetchVideos(); });
  5. Error Handling with new Error Handling with new Always use

    reject() Always use reject() return new Promise(function(resolve, reject) { if (someCondition) { var err = new Error('Not good'); reject(err); } });
  6. Error Handling with method() Error Handling with method() Throw away

    Throw away ClipEntity.prototype.showVideos = Promise.method(function() { if(someCondition) { throw new Error('ops'); } });
  7. REMEMBER REMEMBER returning a Promise is returning a Promise is

    CRITICAL! CRITICAL! But you may return any value as well
  8. Using method() Using method() Anything you return will be the

    resolving value Anything you return will be the resolving value Foo.prototype.one = Promise.method(function() { return this.two(); }); Foo.prototype.two = Promise.method(function() { return 5; }); /* ... */ foo.one() .then(function(res) { console.log(res); // 5 });
  9. Consuming a Promise Consuming a Promise entity.create({name: 'thanasis'}) .then(function(udo) {

    udo.name === 'thanasis'; // true }) .catch(function(error) { // deal with error. });
  10. Composing Composing ClipEntity.prototype._process = function(data) { var clipProcessEnt = new

    ClipProcessEnt(data); return clipProcessEnt.parseFiles() .bind(clipProcessEnt) .then(clipProcessEnt.isFileVideo) .then(clipProcessEnt.storeClip) .then(clipProcessEnt.storeThumb) .then(clipProcessEnt.updateDatabase) .then(clipProcessEnt.removeSourceFiles) .catch(clipProcessEnt.rollback); }; Create master methods <--- Notice the .bind() !!
  11. Error Chaining Error Chaining Process.prototype.rollback = Promise.method(function(err) { log.finer('rollback() ::

    Removing Clip from DB, clipId:', this.clipId); return this.clipEnt.delete(this.clipId) .bind(this) .catch(function (error) { log.warn('rollback() :: NOT GOOD ERROR. File:', this.sourceFilePath, ' Failed to remove record for clip with ID:', this.clipId, 'Error: throw error; }) .then(function() { throw err; }); }); The Rollback <--- From clipEnt.delete() <--- From Parent
  12. Returning Custom Value Returning Custom Value ClipEntity.prototype._process = function(data) {

    var clipProcessEnt = new ClipProcessEnt(data); return clipProcessEnt.parseFiles() .bind(clipProcessEnt) .then(clipProcessEnt.isFileVideo) .then(clipProcessEnt.storeClip) .then(clipProcessEnt.storeThumb) .then(clipProcessEnt.updateDatabase) .then(clipProcessEnt.removeSourceFiles) .return(data) .catch(clipProcessEnt.rollback); }; <-- Here it is!
  13. Running in Parallel Running in Parallel ClipEntity.prototype._process = function(data) {

    var clipProcessEnt = new ClipProcessEnt(data); var promises = []; promises.push(clipProcessEnt.isFileVideo()); promises.push(clipProcessEnt.storeClip()); promises.push(clipProcessEnt.storeThumb()); promises.push(clipProcessEnt.updateDatabase()); return Promise.all(promises) .catch(clipProcessEnt.rollback); };
  14. Running in Parallel Running in Parallel ClipEntity.prototype._process = function(data) {

    var clipProcessEnt = new ClipProcessEnt(data); var promises = []; promises.push(clipProcessEnt.isFileVideo()); promises.push(clipProcessEnt.storeClip()); promises.push(clipProcessEnt.storeThumb()); promises.push(clipProcessEnt.updateDatabase()); return Promise.all(promises, {concurrency: 10}) .catch(clipProcessEnt.rollback); }; ... with a throttle
  15. Applying an Array to a Promise Applying an Array to

    a Promise // Compact ClipExport.prototype._copyClips = Promise.method(function (filenames) { return Promise.map(filenames, this._copyClip.bind(this)); }); // Expanded, better ClipExport.prototype._copyClips = Promise.method(function (filenames) { return Promise.resolve(filenames) .map(this._copyClip); // can chain easier now, error catch, etc });
  16. Multiple items in an Array Multiple items in an Array

    // Compact ClipExport.prototype._copyClips = Promise.method(function (filenames) { return Promise.map(filenames, this._copyClip.bind(this), {concurrency: 10}); }); // Expanded, better ClipExport.prototype._copyClips = Promise.method(function (filenames) { return Promise.resolve(filenames) .map(this._copyClip, {concurrency: 10}); // can chain easier now, error catch, etc }); ... with a throttle
  17. Promisify Promisify var Promise = require('bluebird'); // Promisify all methods

    of a module var fs = Promise.promisifyAll(require('fs')); fs.readFileAsync('one').then().catch(); // Promisify a single method var readFileAsync = Promise.promisify(fs.readFile); readFileAsync.then().catch();
  18. The spread() The spread() Foo.prototype.one = Promise.method(function () { return

    Promise.resolve([ 'one', 'two' ]) map(function(val) { return val + '-lol'; }) .spread(function(arg1, arg2) { console.log(arg1, arg2); // prints: "one-lol two-lol" }); });
  19. All Together All Together Foo.prototype.one = Promise.method(function (data) { this.readAllFilesFor(data)

    .bind(this) .map(this.copyFile) .then(function(filenames) { return Promise.all([ this.process(filenames), this.save(filenames), this.notify(filenames), ]); }) .spread(function(fromProcess, fromSave, fromNotify) { return this.massup(fromProcess, fromSave, fromNotify) }) .then(this.compress) .then(this.sign) .then(this.publish) .catch(this.onError); }); <-- filenames is the result of copyfiles() <-- Parallel Async OPs <-- spread() is one to one with the array