Josherich's Blog

HOME PROJECTS DRAWING ABOUT RSS

Javascript Promise in Bullet Points

02 Feb 2017

  • Promise is about spec and implementations(then/promise, q, bluebird)

  • Beside enabling async, Promise is another way to make function calling look nicer, kinda like pipe, instead of f(g(x)), do x -> f -> g

  • promise.then(undefined, onRejected) is exactly the same as promise.catch(onRejected).

  • Build your own way of handling return value(like return a reason code) when promise chain is complicated. For example, if the promise chain continues after catch(), you need to handle return value from both previous then()s and catch()s

  • Instead of new Promise(), use Promise.resolve() to start promise chain

  • Deferred is just a wrapper of promise, it resolve() or reject() wherever you need in your code, while promise resolve() or reject() inside callbacks

  • Promise chain works because each .then() or .catch() return a new promise

  • To use promise in IE8 and below, replace promise.catch() with promise['catch']()

  • In Promise.race, other promises won’t stop or abort when first promise get fullfilled

  • There is NO abort in ES6 Promise, while some libraries(bluebird) DO implement abort. Of course, you can implement it with lines of code like this gist

  • ES6 Promise comes from Promise/A+, which comes from CommonJS group(famous for CommonJS module spec)

  • When unhandled rejection happens in promise callbacks, all you see is something like Promise rejected with no stack info.

  • a promise visualization

  • Export promise, reference

    function defer() {
      var res, rej;
    
      var promise = new Promise((resolve, reject) => {
        res = resolve;
        rej = reject;
      });
    
      promise.resolve = res;
      promise.reject = rej;
    
      return promise;
    }
    
    this.treeBuilt = defer();
    
    // Many, many lines below…
    
    this.treeBuilt.resolve();
    
  • There is no elegant way to tell if a promise is resolved
    • given you have access to the promise, of course you can do:
      let isResolved = false;
      p.then(function() {
        isResolved = true;
      });
      
    • if you don’t have access to it:
      const marker = {}
      async function isResolved(p) {
        return (await Promise.race([p, marker])) != marker
      }
      
  • Minimum implementation
function Promise() {
  this._callbacks = [];
  this._errCallbacks = [];

  this._resolved = 0;
  this._result = null;
}

Promise.prototype.resolve = function (err, res) {
  if (!err) {
    this._resolved = 1;
    this._result = res;

    for (var i = 0; i < this._callbacks.length; ++i) {
      this._callbacks[i](res);
    }
  } else {
    this._resolved = 2;
    this._result = err;

    for (var iE = 0; iE < this._errCallbacks.length; ++iE) {
      this._errCallbacks[iE](res);
    }
  }

  this._callbacks = [];
  this._errCallbacks = [];
};

Promise.prototype.then = function (cb, errCb) {
  // result
  if (this._resolved === 1) {
    if (cb) {
      cb(this._result);
    }
    return;
  // error
  } else if (this._resolved === 2) {
    if (errCb) {
      errCb(this._result);
    }
    return;
  }

  if (cb) {
    this._callbacks[this._callbacks.length] = cb;
  }

  if (errCb) {
    this._errCallbacks[this._errCallbacks.length] = errCb;
  }
  return this;
};