Skip to content

Instantly share code, notes, and snippets.

@unscriptable
Created February 7, 2011 06:02
Show Gist options
  • Save unscriptable/814052 to your computer and use it in GitHub Desktop.
Save unscriptable/814052 to your computer and use it in GitHub Desktop.
A minimalist implementation of a javascript promise
// (c) copyright unscriptable.com / John Hann
// License MIT
// For more robust promises, see https://github.com/briancavalier/when.js.
function Promise () {
this._thens = [];
}
Promise.prototype = {
/* This is the "front end" API. */
// then(onResolve, onReject): Code waiting for this promise uses the
// then() method to be notified when the promise is complete. There
// are two completion callbacks: onReject and onResolve. A more
// robust promise implementation will also have an onProgress handler.
then: function (onResolve, onReject) {
// capture calls to then()
this._thens.push({ resolve: onResolve, reject: onReject });
},
// Some promise implementations also have a cancel() front end API that
// calls all of the onReject() callbacks (aka a "cancelable promise").
// cancel: function (reason) {},
/* This is the "back end" API. */
// resolve(resolvedValue): The resolve() method is called when a promise
// is resolved (duh). The resolved value (if any) is passed by the resolver
// to this method. All waiting onResolve callbacks are called
// and any future ones are, too, each being passed the resolved value.
resolve: function (val) { this._complete('resolve', val); },
// reject(exception): The reject() method is called when a promise cannot
// be resolved. Typically, you'd pass an exception as the single parameter,
// but any other argument, including none at all, is acceptable.
// All waiting and all future onReject callbacks are called when reject()
// is called and are passed the exception parameter.
reject: function (ex) { this._complete('reject', ex); },
// Some promises may have a progress handler. The back end API to signal a
// progress "event" has a single parameter. The contents of this parameter
// could be just about anything and is specific to your implementation.
// progress: function (data) {},
/* "Private" methods. */
_complete: function (which, arg) {
// switch over to sync then()
this.then = which === 'resolve' ?
function (resolve, reject) { resolve && resolve(arg); } :
function (resolve, reject) { reject && reject(arg); };
// disallow multiple calls to resolve or reject
this.resolve = this.reject =
function () { throw new Error('Promise already completed.'); };
// complete all waiting (async) then()s
var aThen, i = 0;
while (aThen = this._thens[i++]) { aThen[which] && aThen[which](arg); }
delete this._thens;
}
};
@j-norman
Copy link

j-norman commented Apr 8, 2014

You've done a few wonderful things in this to keep the code incredibly short, and yet it is still very readable. Props, and thank you.

@jmas
Copy link

jmas commented Apr 28, 2015

What about catch() for wrap errors throws?

@sukhmeet2390
Copy link

sukhmeet2390 commented Oct 25, 2016

Promises can only be resolved by function which has created it. How does it takes care of that.

function something(){
  var p = new Promise() ;
  setTimeout(function(){
    doSomeAsyncXhr().then(function(){
     p.resolve();
   });
  },5000);
 return p;
}

var p = something();
p.resolve();

@jimmywarting
Copy link

Yours are more like a deferred promise...
So i wanted to try write a similar a+ promise and only try to do the bare minimum

This is what i ended up with when it was minified (hand crafted):

TinyPromise = (h, f = [], b = -1, g, c, l) => (
  l = d => {for (g = d; c = f.shift();) c[b] && c[b](d)},
  // execute the handler(resolve, reject)
  h(d => l(d, b = 0), d => l(d, b = 1)),
  // return a thenable (non chainable)
  {then(...d){ ~b ? d[b] && d[b](g) : f.push(d)} }
)

usage:

TinyPromise((resolve, reject) => resolve(3)).then(console.log, console.error)

Someone wants to try to beat this or try to make it a little better? 😜
Goal is not to make it complete spec compatible but to make it do just what you need

@rafa8626
Copy link

@jimmywarting I think it will be better to post the uncompressed version so you have better feedback about this

@matthieusieben
Copy link

matthieusieben commented Aug 8, 2017

How about one that is chainable, that returns an actual instance ?

function P (cb) {
  var q = [], v, u, ok, complete = function (m, r) {
    if (q) {
      var i = -1, l = q;
      ok = !m; v = r; q = null;
      while (++i < l.length) l[i][m](v);
    }
  };

  cb(complete.bind(u, 0), complete.bind(u, 1));

  this.then = then;
  this.catch = then.bind(u, u)

  function then(success, error) {
    return new P(function (resolve, reject) {
      if (q) q.push([ done.bind(u, success), done.bind(u, error) ]);
      else done(ok ? success : error);

      function done (cb) { try {
        var val = cb ? cb(v) : u;
        if (val && val.then) val.then(resolve, reject);
        else (cb || ok ? resolve : reject)(val);
      } catch (e) { reject(e) } }
    })
  }
}

@souparno
Copy link

souparno commented Nov 17, 2018

i guess a much more simpler implementation could be this

var Promise = function() {
    var this._callBacks = [];

    this.then = function(fn) {
        _callBacks.push(fn);
        return this;
    }

    this.resolve = function(data, err) {
        this._callBacks[0].call(this, data, err);
        this._callBacks.shift();
    }
}



function readPromise(filename) {
    var promise = new Promise();

    fs.readFile(filename, function(err, data) {
        promise.resolve(data, err);
    });
    return promise;
}

readPromise("./myfile.js")
    .then(function(data, err) {
        //-- do something here -- //
        //-- resolve promise with this.resolve(data, err) -- //
    })
    .then(function(data, err) {
        //-- do something here -- //
    });

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