|
/*! |
|
* Resolvr - A Promise Simply JavaScript Library v0.3 |
|
* https://github.com/composite |
|
* usage: new Resolvr(asyncfunc).then(afterfunc); |
|
* |
|
* Copyright 2014 Composite (ukjinplant@msn.com) |
|
* Released under the MIT license |
|
* |
|
* Changelog |
|
* 0.1 Initial release. |
|
* 0.2A Fixed error handling arguments and common argument discriptor. |
|
* 0.2B Fixed pending event and static function returns. |
|
* 0.2C Fixed Exception handling while thenable call when reject. |
|
* 0.3 Thenable callback makes more friendly with Promise. and changed as: (2015-01-22) |
|
* function callback(resolve, reject){ |
|
* this;//will accept similar of Resolvr object but you can accept only these properties: |
|
* this.resolved;//before callback has been resolved. realonly. |
|
* this.rejected;//before callback has been rejected. readonly. |
|
* resolve();//call next callback if Promise was resolved. same as Proimse. |
|
* reject(); //call last callback if Promise was rejected. same as Proimse except calling last callback. |
|
* } |
|
* Delete private util functions make public. |
|
* |
|
* Date: 2014-01-20 |
|
*/ |
|
var Resolvr = (function () { |
|
'use strict'; |
|
// is Your browser supports Promise? |
|
//var isPromise = !!window.Promise; |
|
|
|
// Resolvr utility |
|
var timeout = setTimeout, |
|
untimeout = clearTimeout, |
|
interval = setInterval, |
|
uninterval = clearInterval, |
|
util = { |
|
//context functoin binder |
|
bind: function(fn, context, abort){ |
|
if(!util.isFn(fn)) return; |
|
return function(){ |
|
return fn.apply(context, arguments); |
|
//if(abort) throw new AlreadyCalledError(abort); |
|
}; |
|
// Resolvr argument definition. |
|
},shifted: function (context, args) { |
|
var result = [].slice.call(args); |
|
result.unshift(util.bind(context.resolve, context, 'resolve'), util.bind(context.reject, context, 'reject')); |
|
return result; |
|
//make sandbox with readonly properties |
|
},readonly: function(context){ |
|
return { |
|
count: context.count, |
|
resolved: context.resolved, |
|
rejected: context.rejected |
|
}; |
|
// fn is Function? |
|
}, isFn: function (fn) { |
|
return typeof(fn) === 'function'; |
|
// promise caller (for internal use) |
|
}, caller: function (context, args, isreject) { |
|
//console.log('calling ' + context.queue.length); |
|
timeout(function () { |
|
try{ |
|
var next = context.queue.shift(), |
|
ronly = util.readonly(context), |
|
result; |
|
|
|
//pending first |
|
if(!isreject) context.doing[0].apply(ronly, next[1].slice(2)); |
|
//after then |
|
try{ |
|
result = next ? next[0].apply(ronly, next[0] == context.catched ? next[1].slice(2) : next[1]) : undefined; |
|
}catch(E){ |
|
if(!(E instanceof AlreadyCalledError)) throw E; |
|
} |
|
//stack call for parallel |
|
if(result != undefined && result != null && context.queue.length > 1) { |
|
// result is resolvr or Thenable? go promise ahead. |
|
if(result instanceof Resolvr || typeof(result.then) === 'function') |
|
result.then(function () { |
|
util.caller(context); |
|
}); |
|
else util.caller(context); |
|
} |
|
}catch(E) {// Error will be reject and catch fn called. |
|
//console.log(context); |
|
//console.error(E); |
|
context.queue.push([context.catched, [E, this]]); |
|
context.reject.call(context, E); |
|
//context.catched[0].apply(context, [E].concat(context.catched[1])); |
|
} |
|
}, 0); |
|
|
|
} |
|
}; |
|
|
|
// Safe Exception: when resolved or reject called, procedure call will be interrupt. |
|
function AlreadyCalledError(type){ |
|
this.name = "AlreadyCalledError"; |
|
this.message = "function aborted by " + type + " calling."; |
|
} |
|
AlreadyCalledError.prototype = new Error(); |
|
AlreadyCalledError.prototype.constructor = AlreadyCalledError; |
|
|
|
// Resolvr instance |
|
function Resolvr(fn) { |
|
if(!(this instanceof Resolvr)) return Resolvr.apply(new Resolvr(), arguments); |
|
this.queue = []; // event queue |
|
this.count = 0; // event count |
|
this.catched = function (e) {console.error(e);}; // when error |
|
this.doing = [function () {}, [this]]; // when calling |
|
this.resolved = false; |
|
this.rejected = false; |
|
if(util.isFn(fn)) // regist first calling |
|
this.queue.push([fn, util.shifted(this, [].slice.call(arguments, 1))]); |
|
}; |
|
// Resolvr members |
|
Resolvr.prototype = { |
|
// Resolvr.then : fn will calling when resolvr promise has been resolved |
|
then: function (fn) { |
|
//console.log('then ' + this.count); |
|
if(util.isFn(fn)) |
|
this.queue.push([fn, util.shifted(this, [].slice.call(arguments, 1))]); |
|
if(!this.count++) |
|
util.caller(this, this.queue); |
|
return this; |
|
// Resolvr.resolve : explicit call that your resolvr promise was resolved. |
|
}, resolve: function () { |
|
//console.log('resolve ' + this.count); |
|
//util.caller(this, this.queue); |
|
if(!--this.count || this.queue.length > 1) { |
|
//console.log(this.count); |
|
this.resolved = !(this.rejected = false); |
|
this.queue[0][1] = util.shifted(this, arguments); |
|
util.caller(this, this.queue); |
|
} |
|
|
|
return this; |
|
// Resolvr.catcher : if your call is getting an error, call your fn. |
|
}, catcher: function (fn) { |
|
if(util.isFn(fn)) |
|
this.catched = fn; |
|
return this; |
|
// Resolvr.pending : call while promise is calling. |
|
}, pending: function (fn) { |
|
if(util.isFn(fn)) |
|
this.doing = [fn, util.shifted(this, arguments)]; |
|
return this; |
|
// Resolvr.reject : call when promise is rejected, remaining then will be removed and not fired. |
|
}, reject: function () { |
|
if(!--this.count || this.queue.length > 1) { |
|
//console.log(this.count); |
|
this.rejected = !(this.resolved = false); |
|
this.queue = [this.queue.pop()]; |
|
this.queue[0][1] = util.shifted(this, arguments); |
|
util.caller(this, this.queue, true); |
|
} |
|
return this; |
|
} |
|
}; |
|
// Resolvr.queue (static) : call your promise step by step and return next promise. |
|
Resolvr.queue = function (fns) { |
|
var pros = new Resolvr(fns[0]); |
|
for(var i=0; i<fns.length; i++) { |
|
if(i+1<fns.length) { |
|
(function (next) { |
|
pros.then(fns[next]); |
|
})(i+1); |
|
}else{ |
|
return pros; |
|
} |
|
} |
|
return pros; |
|
}; |
|
// Resolvr.parallel (static) : call your promise together and return next promise. |
|
Resolvr.parallel = function (fns) { |
|
var pros = new Resolvr(function () {return fns[0].apply(this, arguments);}); |
|
for(var i=0; i<fns.length; i++) { |
|
if(i+1<fns.length) { |
|
(function (next) { |
|
pros.then(function () {return fns[next].apply(this, arguments);}); |
|
})(i+1); |
|
}else{ |
|
return pros; |
|
} |
|
} |
|
return pros; |
|
}; |
|
Resolvr.util = { |
|
timeout: timeout, untimeout: untimeout, |
|
interval: interval, uninterval: uninterval |
|
}; |
|
return Resolvr; |
|
|
|
//End of Resolvr.js |
|
|
|
})(); |