Deferred.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. function isArray(arr) {
  2. return Object.prototype.toString.call(arr) === '[object Array]'
  3. }
  4. function foreach(arr, handler) {
  5. if (isArray(arr)) {
  6. for (var i = 0; i < arr.length; i++) {
  7. handler(arr[i])
  8. }
  9. } else handler(arr)
  10. }
  11. function D(fn) {
  12. var status = 'pending',
  13. doneFuncs = [],
  14. failFuncs = [],
  15. progressFuncs = [],
  16. resultArgs = null,
  17. promise = {
  18. done: function () {
  19. for (var i = 0; i < arguments.length; i++) {
  20. // skip any undefined or null arguments
  21. if (!arguments[i]) {
  22. continue
  23. }
  24. if (isArray(arguments[i])) {
  25. var arr = arguments[i]
  26. for (var j = 0; j < arr.length; j++) {
  27. // immediately call the function if the deferred has been resolved
  28. if (status === 'resolved') {
  29. arr[j].apply(this, resultArgs)
  30. }
  31. doneFuncs.push(arr[j])
  32. }
  33. } else {
  34. // immediately call the function if the deferred has been resolved
  35. if (status === 'resolved') {
  36. arguments[i].apply(this, resultArgs)
  37. }
  38. doneFuncs.push(arguments[i])
  39. }
  40. }
  41. return this
  42. },
  43. fail: function () {
  44. for (var i = 0; i < arguments.length; i++) {
  45. // skip any undefined or null arguments
  46. if (!arguments[i]) {
  47. continue
  48. }
  49. if (isArray(arguments[i])) {
  50. var arr = arguments[i]
  51. for (var j = 0; j < arr.length; j++) {
  52. // immediately call the function if the deferred has been resolved
  53. if (status === 'rejected') {
  54. arr[j].apply(this, resultArgs)
  55. }
  56. failFuncs.push(arr[j])
  57. }
  58. } else {
  59. // immediately call the function if the deferred has been resolved
  60. if (status === 'rejected') {
  61. arguments[i].apply(this, resultArgs)
  62. }
  63. failFuncs.push(arguments[i])
  64. }
  65. }
  66. return this
  67. },
  68. always: function () {
  69. return this.done.apply(this, arguments).fail.apply(this, arguments)
  70. },
  71. progress: function () {
  72. for (var i = 0; i < arguments.length; i++) {
  73. // skip any undefined or null arguments
  74. if (!arguments[i]) {
  75. continue
  76. }
  77. if (isArray(arguments[i])) {
  78. var arr = arguments[i]
  79. for (var j = 0; j < arr.length; j++) {
  80. // immediately call the function if the deferred has been resolved
  81. if (status === 'pending') {
  82. progressFuncs.push(arr[j])
  83. }
  84. }
  85. } else {
  86. // immediately call the function if the deferred has been resolved
  87. if (status === 'pending') {
  88. progressFuncs.push(arguments[i])
  89. }
  90. }
  91. }
  92. return this
  93. },
  94. then: function (done, fail, progress) {
  95. /*
  96. // fail callbacks
  97. if (arguments.length > 1 && arguments[1]) {
  98. this.fail(arguments[1])
  99. }
  100. // done callbacks
  101. if (arguments.length > 0 && arguments[0]) {
  102. this.done(arguments[0])
  103. }
  104. // notify callbacks
  105. if (arguments.length > 2 && arguments[2]) {
  106. this.progress(arguments[2])
  107. }
  108. return this
  109. */
  110. return D(function (def) {
  111. foreach(done, function (func) {
  112. // filter function
  113. if (typeof func === 'function') {
  114. deferred.done(function () {
  115. var returnval = func.apply(this, arguments)
  116. // if a new deferred/promise is returned, its state is passed to the current deferred/promise
  117. if (returnval && typeof returnval === 'function') {
  118. returnval.promise().then(def.resolve, def.reject, def.notify)
  119. } else {
  120. // if new return val is passed, it is passed to the piped done
  121. def.resolve(returnval)
  122. }
  123. })
  124. } else {
  125. deferred.done(def.resolve)
  126. }
  127. })
  128. foreach(fail, function (func) {
  129. if (typeof func === 'function') {
  130. deferred.fail(function () {
  131. var returnval = func.apply(this, arguments)
  132. if (returnval && typeof returnval === 'function') {
  133. returnval.promise().then(def.resolve, def.reject, def.notify)
  134. } else {
  135. def.reject(returnval)
  136. }
  137. })
  138. } else {
  139. deferred.fail(def.reject)
  140. }
  141. })
  142. }).promise()
  143. },
  144. catch: function () {
  145. for (var i = 0; i < arguments.length; i++) {
  146. // skip any undefined or null arguments
  147. if (!arguments[i]) {
  148. continue
  149. }
  150. if (isArray(arguments[i])) {
  151. var arr = arguments[i]
  152. for (var j = 0; j < arr.length; j++) {
  153. // immediately call the function if the deferred has been resolved
  154. if (status === 'rejected') {
  155. arr[j].apply(this, resultArgs)
  156. }
  157. failFuncs.push(arr[j])
  158. }
  159. } else {
  160. // immediately call the function if the deferred has been resolved
  161. if (status === 'rejected') {
  162. arguments[i].apply(this, resultArgs)
  163. }
  164. failFuncs.push(arguments[i])
  165. }
  166. }
  167. return this
  168. },
  169. promise: function (obj) {
  170. if (obj == null) {
  171. return promise
  172. } else {
  173. for (var i in promise) {
  174. obj[i] = promise[i]
  175. }
  176. return obj
  177. }
  178. },
  179. state: function () {
  180. return status
  181. },
  182. debug: function () {
  183. console.log('[debug]', doneFuncs, failFuncs, status)
  184. },
  185. isRejected: function () {
  186. return status === 'rejected'
  187. },
  188. isResolved: function () {
  189. return status === 'resolved'
  190. },
  191. pipe: function (done, fail, progress) {
  192. return D(function (def) {
  193. foreach(done, function (func) {
  194. // filter function
  195. if (typeof func === 'function') {
  196. deferred.done(function () {
  197. var returnval = func.apply(this, arguments)
  198. // if a new deferred/promise is returned, its state is passed to the current deferred/promise
  199. if (returnval && typeof returnval === 'function') {
  200. returnval.promise().then(def.resolve, def.reject, def.notify)
  201. } else {
  202. // if new return val is passed, it is passed to the piped done
  203. def.resolve(returnval)
  204. }
  205. })
  206. } else {
  207. deferred.done(def.resolve)
  208. }
  209. })
  210. foreach(fail, function (func) {
  211. if (typeof func === 'function') {
  212. deferred.fail(function () {
  213. var returnval = func.apply(this, arguments)
  214. if (returnval && typeof returnval === 'function') {
  215. returnval.promise().then(def.resolve, def.reject, def.notify)
  216. } else {
  217. def.reject(returnval)
  218. }
  219. })
  220. } else {
  221. deferred.fail(def.reject)
  222. }
  223. })
  224. }).promise()
  225. },
  226. },
  227. deferred = {
  228. resolveWith: function (context) {
  229. if (status === 'pending') {
  230. status = 'resolved'
  231. var args = (resultArgs = arguments.length > 1 ? arguments[1] : [])
  232. for (var i = 0; i < doneFuncs.length; i++) {
  233. doneFuncs[i].apply(context, args)
  234. }
  235. }
  236. return this
  237. },
  238. rejectWith: function (context) {
  239. if (status === 'pending') {
  240. status = 'rejected'
  241. var args = (resultArgs = arguments.length > 1 ? arguments[1] : [])
  242. for (var i = 0; i < failFuncs.length; i++) {
  243. failFuncs[i].apply(context, args)
  244. }
  245. }
  246. return this
  247. },
  248. notifyWith: function (context) {
  249. if (status === 'pending') {
  250. var args = (resultArgs = arguments.length > 1 ? arguments[1] : [])
  251. for (var i = 0; i < progressFuncs.length; i++) {
  252. progressFuncs[i].apply(context, args)
  253. }
  254. }
  255. return this
  256. },
  257. resolve: function () {
  258. return this.resolveWith(this, arguments)
  259. },
  260. reject: function () {
  261. return this.rejectWith(this, arguments)
  262. },
  263. notify: function () {
  264. return this.notifyWith(this, arguments)
  265. },
  266. resolveValue: function(val) {
  267. status = 'resolved'
  268. var args = (resultArgs = val == void 0 ? [] : [val])
  269. for (var i = 0; i < doneFuncs.length; i++) {
  270. doneFuncs[i].apply(this, args)
  271. }
  272. return this
  273. }
  274. }
  275. var obj = promise.promise(deferred)
  276. if (fn) {
  277. fn.apply(obj, [obj])
  278. }
  279. return obj
  280. }
  281. D.when = function () {
  282. if (arguments.length < 2) {
  283. var obj = arguments.length ? arguments[0] : undefined
  284. if (obj && typeof obj.isResolved === 'function' && typeof obj.isRejected === 'function') {
  285. return obj.promise()
  286. } else {
  287. return D().resolve(obj).promise()
  288. }
  289. } else {
  290. return (function (args) {
  291. var df = D(),
  292. size = args.length,
  293. done = 0,
  294. 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
  295. for (var i = 0; i < args.length; i++) {
  296. ;(function (j) {
  297. var obj = null
  298. if (args[j].done) {
  299. args[j]
  300. .done(function () {
  301. rp[j] = arguments.length < 2 ? arguments[0] : arguments
  302. if (++done == size) {
  303. df.resolve.apply(df, rp)
  304. }
  305. })
  306. .fail(function () {
  307. df.reject(arguments)
  308. })
  309. } else {
  310. obj = args[j]
  311. args[j] = new Deferred()
  312. args[j]
  313. .done(function () {
  314. rp[j] = arguments.length < 2 ? arguments[0] : arguments
  315. if (++done == size) {
  316. df.resolve.apply(df, rp)
  317. }
  318. })
  319. .fail(function () {
  320. df.reject(arguments)
  321. })
  322. .resolve(obj)
  323. }
  324. })(i)
  325. }
  326. return df.promise()
  327. })(arguments)
  328. }
  329. }
  330. export const Defer = D
  331. export default function () {
  332. return new D()
  333. }