function isArray(arr) { return Object.prototype.toString.call(arr) === '[object Array]' } function foreach(arr, handler) { if (isArray(arr)) { for (var i = 0; i < arr.length; i++) { handler(arr[i]) } } else handler(arr) } function D(fn) { var status = 'pending', doneFuncs = [], failFuncs = [], progressFuncs = [], resultArgs = null, promise = { done: function () { for (var i = 0; i < arguments.length; i++) { // skip any undefined or null arguments if (!arguments[i]) { continue } if (isArray(arguments[i])) { var arr = arguments[i] for (var j = 0; j < arr.length; j++) { // immediately call the function if the deferred has been resolved if (status === 'resolved') { arr[j].apply(this, resultArgs) } doneFuncs.push(arr[j]) } } else { // immediately call the function if the deferred has been resolved if (status === 'resolved') { arguments[i].apply(this, resultArgs) } doneFuncs.push(arguments[i]) } } return this }, fail: function () { for (var i = 0; i < arguments.length; i++) { // skip any undefined or null arguments if (!arguments[i]) { continue } if (isArray(arguments[i])) { var arr = arguments[i] for (var j = 0; j < arr.length; j++) { // immediately call the function if the deferred has been resolved if (status === 'rejected') { arr[j].apply(this, resultArgs) } failFuncs.push(arr[j]) } } else { // immediately call the function if the deferred has been resolved if (status === 'rejected') { arguments[i].apply(this, resultArgs) } failFuncs.push(arguments[i]) } } return this }, always: function () { return this.done.apply(this, arguments).fail.apply(this, arguments) }, progress: function () { for (var i = 0; i < arguments.length; i++) { // skip any undefined or null arguments if (!arguments[i]) { continue } if (isArray(arguments[i])) { var arr = arguments[i] for (var j = 0; j < arr.length; j++) { // immediately call the function if the deferred has been resolved if (status === 'pending') { progressFuncs.push(arr[j]) } } } else { // immediately call the function if the deferred has been resolved if (status === 'pending') { progressFuncs.push(arguments[i]) } } } return this }, then: function (done, fail, progress) { /* // fail callbacks if (arguments.length > 1 && arguments[1]) { this.fail(arguments[1]) } // done callbacks if (arguments.length > 0 && arguments[0]) { this.done(arguments[0]) } // notify callbacks if (arguments.length > 2 && arguments[2]) { this.progress(arguments[2]) } return this */ return D(function (def) { foreach(done, function (func) { // filter function if (typeof func === 'function') { deferred.done(function () { var returnval = func.apply(this, arguments) // if a new deferred/promise is returned, its state is passed to the current deferred/promise if (returnval && typeof returnval === 'function') { returnval.promise().then(def.resolve, def.reject, def.notify) } else { // if new return val is passed, it is passed to the piped done def.resolve(returnval) } }) } else { deferred.done(def.resolve) } }) foreach(fail, function (func) { if (typeof func === 'function') { deferred.fail(function () { var returnval = func.apply(this, arguments) if (returnval && typeof returnval === 'function') { returnval.promise().then(def.resolve, def.reject, def.notify) } else { def.reject(returnval) } }) } else { deferred.fail(def.reject) } }) }).promise() }, catch: function () { for (var i = 0; i < arguments.length; i++) { // skip any undefined or null arguments if (!arguments[i]) { continue } if (isArray(arguments[i])) { var arr = arguments[i] for (var j = 0; j < arr.length; j++) { // immediately call the function if the deferred has been resolved if (status === 'rejected') { arr[j].apply(this, resultArgs) } failFuncs.push(arr[j]) } } else { // immediately call the function if the deferred has been resolved if (status === 'rejected') { arguments[i].apply(this, resultArgs) } failFuncs.push(arguments[i]) } } return this }, promise: function (obj) { if (obj == null) { return promise } else { for (var i in promise) { obj[i] = promise[i] } return obj } }, state: function () { return status }, debug: function () { console.log('[debug]', doneFuncs, failFuncs, status) }, isRejected: function () { return status === 'rejected' }, isResolved: function () { return status === 'resolved' }, pipe: function (done, fail, progress) { return D(function (def) { foreach(done, function (func) { // filter function if (typeof func === 'function') { deferred.done(function () { var returnval = func.apply(this, arguments) // if a new deferred/promise is returned, its state is passed to the current deferred/promise if (returnval && typeof returnval === 'function') { returnval.promise().then(def.resolve, def.reject, def.notify) } else { // if new return val is passed, it is passed to the piped done def.resolve(returnval) } }) } else { deferred.done(def.resolve) } }) foreach(fail, function (func) { if (typeof func === 'function') { deferred.fail(function () { var returnval = func.apply(this, arguments) if (returnval && typeof returnval === 'function') { returnval.promise().then(def.resolve, def.reject, def.notify) } else { def.reject(returnval) } }) } else { deferred.fail(def.reject) } }) }).promise() }, }, deferred = { resolveWith: function (context) { if (status === 'pending') { status = 'resolved' var args = (resultArgs = arguments.length > 1 ? arguments[1] : []) for (var i = 0; i < doneFuncs.length; i++) { doneFuncs[i].apply(context, args) } } return this }, rejectWith: function (context) { if (status === 'pending') { status = 'rejected' var args = (resultArgs = arguments.length > 1 ? arguments[1] : []) for (var i = 0; i < failFuncs.length; i++) { failFuncs[i].apply(context, args) } } return this }, notifyWith: function (context) { if (status === 'pending') { var args = (resultArgs = arguments.length > 1 ? arguments[1] : []) for (var i = 0; i < progressFuncs.length; i++) { progressFuncs[i].apply(context, args) } } return this }, resolve: function () { return this.resolveWith(this, arguments) }, reject: function () { return this.rejectWith(this, arguments) }, notify: function () { return this.notifyWith(this, arguments) }, resolveValue: function(val) { status = 'resolved' var args = (resultArgs = val == void 0 ? [] : [val]) for (var i = 0; i < doneFuncs.length; i++) { doneFuncs[i].apply(this, args) } return this } } var obj = promise.promise(deferred) if (fn) { fn.apply(obj, [obj]) } return obj } D.when = function () { if (arguments.length < 2) { var obj = arguments.length ? arguments[0] : undefined if (obj && typeof obj.isResolved === 'function' && typeof obj.isRejected === 'function') { return obj.promise() } else { return D().resolve(obj).promise() } } else { return (function (args) { var df = D(), size = args.length, done = 0, rp = new Array(size) // resolve params: params of each resolve, we need to track down them to be able to pass them in the correct order if the master needs to be resolved for (var i = 0; i < args.length; i++) { ;(function (j) { var obj = null if (args[j].done) { args[j] .done(function () { rp[j] = arguments.length < 2 ? arguments[0] : arguments if (++done == size) { df.resolve.apply(df, rp) } }) .fail(function () { df.reject(arguments) }) } else { obj = args[j] args[j] = new Deferred() args[j] .done(function () { rp[j] = arguments.length < 2 ? arguments[0] : arguments if (++done == size) { df.resolve.apply(df, rp) } }) .fail(function () { df.reject(arguments) }) .resolve(obj) } })(i) } return df.promise() })(arguments) } } export const Defer = D export default function () { return new D() }