Skip to content

Instantly share code, notes, and snippets.

@sukima
Last active January 2, 2016 11:09
Show Gist options
  • Save sukima/8294488 to your computer and use it in GitHub Desktop.
Save sukima/8294488 to your computer and use it in GitHub Desktop.
// PinkySwear - Minimalistic implementation of the Promises/A+ spec
//
// Public Domain. Use, modify and distribute it any way you like. No attribution required.
//
// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
//
// https://github.com/timjansen/PinkySwear.js
//
// Modified to add resolve/reject/notify/process/fin/done/fail/get/invoke methods
(function(target) {
/* jshint eqnull:true */
function isFunction(f,o) { return typeof f == 'function'; }
function defer(callback) { setTimeout(callback, 0); }
target[0][target[1]] = function pinkySwear() {
var state; // undefined/null = pending, true = fulfilled, false = rejected
var values = []; // an array of values as arguments for the then() handlers
var deferred = []; // functions to call when set() is invoked
var progress_fns; // functions to call when notify() is invoked
var set = function promise(newState, newValues) {
if (state == null) {
state = newState;
values = newValues;
defer(function() {
for (var i = 0; i < deferred.length; i++)
deferred[i]();
});
}
};
set.then = function(onFulfilled, onRejected, onProgress) {
var newPromise = pinkySwear();
newPromise.progress = function(v) { set.progress(v); return newPromise; };
newPromise.notify = function(v) { set.notify(v); return newPromise; };
var callCallbacks = function() {
try {
var f = (state ? onFulfilled : onRejected);
if (isFunction(f)) {
var r = f.apply(null, values);
if (r && isFunction(r.then))
r.then(
function(value){newPromise(true, [value]);},
function(value){newPromise(false, [value]);},
function(value){newPromise.notify(value);}
);
else
newPromise(true, [r]);
}
else
newPromise(state, values);
}
catch (e) {
newPromise(false, [e]);
}
};
if (state != null)
defer(callCallbacks);
else
deferred.push(callCallbacks);
if (isFunction(onProgress))
set.progress(onProgress);
return newPromise;
};
set.notify = function(value) {
if (state == null)
defer(function() {
if (progress_fns != null)
for (var i = 0; i < progress_fns.length; i++)
progress_fns[i](value);
});
};
set.resolve = function(value) { set(true, [value]); };
set.reject = function(value) { set(false, [value]); };
set.progress = function(onProgress) {
if (progress_fns == null) { progress_fns = []; }
progress_fns.push(onProgress);
return set;
};
// always(func) is the same as then(func, func)
set.always = function(func) { return set.then(func, func); };
// fin(func) is like always() but doesn't modify the promise chain
set.fin = function(func) { set.then(func, func); return set; };
// error(func) is the same as then(0, func)
set.error = set.fail = function(func) { return set.then(0, func); };
function handleUncaughtExceptions() {
if (state === false) {
throw (values.length > 1) ? values : values[0];
}
}
set.done = function(onFulfilled, onRejected, onProgress) {
if (onFulfilled || onRejected || onProgress) {
set.then(onFulfilled, onRejected, onProgress).done();
return;
}
if (state != null)
defer(handleUncaughtExceptions);
else
deferred.push(handleUncaughtExceptions);
};
set.get = function(prop) {
return set.then(function(value) { return value[prop]; });
};
set.invoke = function(prop) {
var args = [].slice.call(arguments, 1) || [];
return set.then(function(value) { return value[prop].apply(value, args); });
};
return set;
}
return pinkySwear;
})(typeof module === 'undefined' ? [this, 'pinkySwear'] : [module, 'exports']);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment