|
@@ -0,0 +1,382 @@
|
|
|
+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()
|
|
|
+}
|