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
Post a Comment