javascript - How is a promise/defer library implemented? -


how promise/defer library q implemented? trying read source code found pretty hard understand, thought it'd great if explain me, high level, techniques used implement promises in single-thread js environments node , browsers.

i find harder explain show example, here simple implementation of defer/promise be.

disclaimer: not functional implementation , parts of promise/a specification missing, explain basis of promises.

tl;dr: go create classes , example section see full implementation.

promise:

first need create promise object array of callbacks. i'll start working objects because it's clearer:

var promise = {   callbacks: [] } 

now add callbacks method then:

var promise = {   callbacks: [],   then: function (callback) {     callbacks.push(callback);   } } 

and need error callbacks too:

var promise = {   okcallbacks: [],   kocallbacks: [],   then: function (okcallback, kocallback) {     okcallbacks.push(okcallback);     if (kocallback) {       kocallbacks.push(kocallback);     }   } } 

defer:

now create defer object have promise:

var defer = {   promise: promise }; 

the defer needs resolved:

var defer = {   promise: promise,   resolve: function (data) {     this.promise.okcallbacks.foreach(function(callback) {       window.settimeout(function () {         callback(data)       }, 0);     });   }, }; 

and needs reject:

var defer = {   promise: promise,   resolve: function (data) {     this.promise.okcallbacks.foreach(function(callback) {       window.settimeout(function () {         callback(data)       }, 0);     });   },    reject: function (error) {     this.promise.kocallbacks.foreach(function(callback) {       window.settimeout(function () {         callback(error)       }, 0);     });   } }; 

note callbacks called in timeout allow code asynchronous.

and that's basic defer/promise implementation needs.

create classes , example:

now lets convert both objects classes, first promise:

var promise = function () {   this.okcallbacks = [];   this.kocallbacks = []; };  promise.prototype = {   okcallbacks: null,   kocallbacks: null,   then: function (okcallback, kocallback) {     okcallbacks.push(okcallback);     if (kocallback) {       kocallbacks.push(kocallback);     }   } }; 

and defer:

var defer = function () {   this.promise = new promise(); };  defer.prototype = {   promise: null,   resolve: function (data) {     this.promise.okcallbacks.foreach(function(callback) {       window.settimeout(function () {         callback(data)       }, 0);     });   },    reject: function (error) {     this.promise.kocallbacks.foreach(function(callback) {       window.settimeout(function () {         callback(error)       }, 0);     });   } }; 

and here example of use:

function test() {   var defer = new defer();   // example of async call   servercall(function (request) {     if (request.status === 200) {       defer.resolve(request.responsetext);     } else {       defer.reject(new error("status code " + request.status));     }   });   return defer.promise; }  test().then(function (text) {   alert(text); }, function (error) {   alert(error.message); }); 

as can see basic parts simple , small. grow when add other options, example multiple promise resolution:

defer.all(promisea, promiseb, promisec).then() 

or promise chaining:

getuserbyid(id).then(getfilesbyuser).then(deletefile).then(promptresult); 

to read more specifications: commonjs promise specification. note main libraries (q, when.js, rsvp.js, node-promise, ...) follow promises/a specification.

hope clear enough.

edit:

as asked in comments, i've added 2 things in version:

  • the possibility call of promise, no matter status has.
  • the possibility chain promises.

to able call promise when resolved need add status promise, , when called check status. if status resolved or rejected execute callback data or error.

to able chain promises need generate new defer each call then and, when promise resolved/rejected, resolve/reject new promise result of callback. when promise done, if callback returns new promise bound promise returned then(). if not, promise resolved result of callback.

here promise:

var promise = function () {   this.okcallbacks = [];   this.kocallbacks = []; };  promise.prototype = {   okcallbacks: null,   kocallbacks: null,   status: 'pending',   error: null,    then: function (okcallback, kocallback) {     var defer = new defer();      // add callbacks arrays defer binded these callbacks     this.okcallbacks.push({       func: okcallback,       defer: defer     });      if (kocallback) {       this.kocallbacks.push({         func: kocallback,         defer: defer       });     }      // check if promise not pending. if not call callback     if (this.status === 'resolved') {       this.executecallback({         func: okcallback,         defer: defer       }, this.data)     } else if(this.status === 'rejected') {       this.executecallback({         func: kocallback,         defer: defer       }, this.error)     }      return defer.promise;   },    executecallback: function (callbackdata, result) {     window.settimeout(function () {       var res = callbackdata.func(result);       if (res instanceof promise) {         callbackdata.defer.bind(res);       } else {         callbackdata.defer.resolve(res);       }     }, 0);   } }; 

and defer:

var defer = function () {   this.promise = new promise(); };  defer.prototype = {   promise: null,   resolve: function (data) {     var promise = this.promise;     promise.data = data;     promise.status = 'resolved';     promise.okcallbacks.foreach(function(callbackdata) {       promise.executecallback(callbackdata, data);     });   },    reject: function (error) {     var promise = this.promise;     promise.error = error;     promise.status = 'rejected';     promise.kocallbacks.foreach(function(callbackdata) {       promise.executecallback(callbackdata, error);     });   },    // make promise behave promise:   // when other promise resolved/rejected resolved/rejected   // same data   bind: function (promise) {     var = this;     promise.then(function (res) {       that.resolve(res);     }, function (err) {       that.reject(err);     })   } }; 

as can see, has grown quite bit.


Comments

Popular posts from this blog

php - Calling a template part from a post -

Firefox SVG shape not printing when it has stroke -

How to mention the localhost in android -