├── .bowerrc ├── .gitattributes ├── Makefile ├── README.md ├── bower.json ├── example.html ├── js ├── deferred.js └── deferred.min.js ├── node_module └── jq-deferred.js └── package.json /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "registry": "https://registry.bower.io" 3 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # npm install uglify-js -g 2 | minify: 3 | uglifyjs -o js/deferred.min.js js/deferred.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | deferred-js 2 | =========== 3 | 4 | deferred-js is a light (less than 1 kb gzipped) standalone implementation of promise/deferred that aims to be fully compatible with $.Deferred found in [jQuery 1.5+] (http://api.jquery.com/category/deferred-object/). 5 | 6 | 7 | What's implemented ? 8 | -------------------- 9 | 10 | * Deferred.always 11 | * Deferred.done 12 | * Deferred.fail 13 | * Deferred.isRejected 14 | * Deferred.isResolved 15 | * Deferred.notify 16 | * Deferred.notifyWith 17 | * Deferred.progress 18 | * Deferred.promise 19 | * Deferred.reject 20 | * Deferred.rejectWith 21 | * Deferred.resolve 22 | * Deferred.resolveWith 23 | * Deferred.state 24 | * Deferred.then 25 | * Deferred.when 26 | 27 | 28 | What's Missing ? 29 | ---------------- 30 | 31 | * Deferred.pipe 32 | 33 | 34 | Setup 35 | ----- 36 | 37 | #### Using nodejs 38 | * type in a terminal: `npm install deferred-js` 39 | * require deferred-js: `var Deferred = require('deferred-js');` 40 | 41 | #### Using a browser 42 | * include the deferred script: `` 43 | * Deferred is available inside the global object 44 | 45 | Example 46 | ------- 47 | 48 | // Create a Deferred and return its Promise 49 | function asyncEvent(){ 50 | var dfd = new Deferred(); 51 | setTimeout(function(){ 52 | dfd.resolve("hurray"); 53 | }, Math.floor(Math.random()*1500)); 54 | setTimeout(function(){ 55 | dfd.reject("sorry"); 56 | }, Math.floor(Math.random()*1500)); 57 | return dfd.promise(); 58 | } 59 | 60 | // Attach a done and fail handler for the asyncEvent 61 | Deferred.when(asyncEvent()).then( 62 | function(status){ 63 | console.log( status+', things are going well' ); 64 | }, 65 | function(status){ 66 | console.log( status+', you fail this time' ); 67 | } 68 | ); 69 | 70 | Licence 71 | ------- 72 | 73 | This software is distributed under an MIT licence. 74 | 75 | Copyright 2012 © Nicolas Ramz 76 | 77 | > Permission is hereby granted, free of charge, to any person obtaining a copy of this software 78 | > and associated documentation files (the "Software"), to deal in the Software without 79 | > restriction, including without limitation the rights to use, copy, modify, merge, publish, 80 | > distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 81 | > Software is furnished to do so, subject to the following conditions: 82 | > The above copyright notice and this permission notice shall be included in all copies or 83 | > substantial portions of the Software. 84 | > The Software is provided "as is", without warranty of any kind, express or implied, including 85 | > but not limited to the warranties of merchantability, fitness for a particular purpose and 86 | > noninfringement. In no event shall the authors or copyright holders be liable for any claim, 87 | > damages or other liability, whether in an action of contract, tort or otherwise, arising from, 88 | > out of or in connection with the software or the use or other dealings in the Software. 89 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deferred-js", 3 | "version": "1.0.4", 4 | "main": "js/deferred.min.js", 5 | "ignore": [ 6 | "node_module", 7 | ".gitattributes", 8 | ".gitignore", 9 | "Makefile", 10 | "example.html", 11 | "bower.json", 12 | "package.json", 13 | "README.md" 14 | ], 15 | "homepage": "https://github.com/warpdesign/deferred-js", 16 | "authors": [ 17 | "Nicolas Ramz (http://nicolasramz.fr)" 18 | ] 19 | } -------------------------------------------------------------------------------- /example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 15 | 16 | 17 | 70 | 71 | 72 |
73 |
74 | 75 | -------------------------------------------------------------------------------- /js/deferred.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | function isArray(arr) { 3 | return Object.prototype.toString.call(arr) === '[object Array]'; 4 | } 5 | 6 | function foreach(arr, handler) { 7 | if (isArray(arr)) { 8 | for (var i = 0; i < arr.length; i++) { 9 | handler(arr[i]); 10 | } 11 | } 12 | else 13 | handler(arr); 14 | } 15 | 16 | function D(fn) { 17 | var status = 'pending', 18 | doneFuncs = [], 19 | failFuncs = [], 20 | progressFuncs = [], 21 | resultArgs = null, 22 | 23 | promise = { 24 | done: function() { 25 | for (var i = 0; i < arguments.length; i++) { 26 | // skip any undefined or null arguments 27 | if (!arguments[i]) { 28 | continue; 29 | } 30 | 31 | if (isArray(arguments[i])) { 32 | var arr = arguments[i]; 33 | for (var j = 0; j < arr.length; j++) { 34 | // immediately call the function if the deferred has been resolved 35 | if (status === 'resolved') { 36 | arr[j].apply(this, resultArgs); 37 | } 38 | 39 | doneFuncs.push(arr[j]); 40 | } 41 | } 42 | else { 43 | // immediately call the function if the deferred has been resolved 44 | if (status === 'resolved') { 45 | arguments[i].apply(this, resultArgs); 46 | } 47 | 48 | doneFuncs.push(arguments[i]); 49 | } 50 | } 51 | 52 | return this; 53 | }, 54 | 55 | fail: function() { 56 | for (var i = 0; i < arguments.length; i++) { 57 | // skip any undefined or null arguments 58 | if (!arguments[i]) { 59 | continue; 60 | } 61 | 62 | if (isArray(arguments[i])) { 63 | var arr = arguments[i]; 64 | for (var j = 0; j < arr.length; j++) { 65 | // immediately call the function if the deferred has been resolved 66 | if (status === 'rejected') { 67 | arr[j].apply(this, resultArgs); 68 | } 69 | 70 | failFuncs.push(arr[j]); 71 | } 72 | } 73 | else { 74 | // immediately call the function if the deferred has been resolved 75 | if (status === 'rejected') { 76 | arguments[i].apply(this, resultArgs); 77 | } 78 | 79 | failFuncs.push(arguments[i]); 80 | } 81 | } 82 | 83 | return this; 84 | }, 85 | 86 | always: function() { 87 | return this.done.apply(this, arguments).fail.apply(this, arguments); 88 | }, 89 | 90 | progress: function() { 91 | for (var i = 0; i < arguments.length; i++) { 92 | // skip any undefined or null arguments 93 | if (!arguments[i]) { 94 | continue; 95 | } 96 | 97 | if (isArray(arguments[i])) { 98 | var arr = arguments[i]; 99 | for (var j = 0; j < arr.length; j++) { 100 | // immediately call the function if the deferred has been resolved 101 | if (status === 'pending') { 102 | progressFuncs.push(arr[j]); 103 | } 104 | } 105 | } 106 | else { 107 | // immediately call the function if the deferred has been resolved 108 | if (status === 'pending') { 109 | progressFuncs.push(arguments[i]); 110 | } 111 | } 112 | } 113 | 114 | return this; 115 | }, 116 | 117 | then: function() { 118 | // fail callbacks 119 | if (arguments.length > 1 && arguments[1]) { 120 | this.fail(arguments[1]); 121 | } 122 | 123 | // done callbacks 124 | if (arguments.length > 0 && arguments[0]) { 125 | this.done(arguments[0]); 126 | } 127 | 128 | // notify callbacks 129 | if (arguments.length > 2 && arguments[2]) { 130 | this.progress(arguments[2]); 131 | } 132 | }, 133 | 134 | promise: function(obj) { 135 | if (obj == null) { 136 | return promise; 137 | } else { 138 | for (var i in promise) { 139 | obj[i] = promise[i]; 140 | } 141 | return obj; 142 | } 143 | }, 144 | 145 | state: function() { 146 | return status; 147 | }, 148 | 149 | debug: function() { 150 | console.log('[debug]', doneFuncs, failFuncs, status); 151 | }, 152 | 153 | isRejected: function() { 154 | return status === 'rejected'; 155 | }, 156 | 157 | isResolved: function() { 158 | return status === 'resolved'; 159 | }, 160 | 161 | pipe: function(done, fail, progress) { 162 | return D(function(def) { 163 | foreach(done, function(func) { 164 | // filter function 165 | if (typeof func === 'function') { 166 | deferred.done(function() { 167 | var returnval = func.apply(this, arguments); 168 | // if a new deferred/promise is returned, its state is passed to the current deferred/promise 169 | if (returnval && typeof returnval === 'function') { 170 | returnval.promise().then(def.resolve, def.reject, def.notify); 171 | } 172 | else { // if new return val is passed, it is passed to the piped done 173 | def.resolve(returnval); 174 | } 175 | }); 176 | } 177 | else { 178 | deferred.done(def.resolve); 179 | } 180 | }); 181 | 182 | foreach(fail, function(func) { 183 | if (typeof func === 'function') { 184 | deferred.fail(function() { 185 | var returnval = func.apply(this, arguments); 186 | 187 | if (returnval && typeof returnval === 'function') { 188 | returnval.promise().then(def.resolve, def.reject, def.notify); 189 | } else { 190 | def.reject(returnval); 191 | } 192 | }); 193 | } 194 | else { 195 | deferred.fail(def.reject); 196 | } 197 | }); 198 | }).promise(); 199 | } 200 | }, 201 | 202 | deferred = { 203 | resolveWith: function(context) { 204 | if (status === 'pending') { 205 | status = 'resolved'; 206 | var args = resultArgs = (arguments.length > 1) ? arguments[1] : []; 207 | for (var i = 0; i < doneFuncs.length; i++) { 208 | doneFuncs[i].apply(context, args); 209 | } 210 | } 211 | return this; 212 | }, 213 | 214 | rejectWith: function(context) { 215 | if (status === 'pending') { 216 | status = 'rejected'; 217 | var args = resultArgs = (arguments.length > 1) ? arguments[1] : []; 218 | for (var i = 0; i < failFuncs.length; i++) { 219 | failFuncs[i].apply(context, args); 220 | } 221 | } 222 | return this; 223 | }, 224 | 225 | notifyWith: function(context) { 226 | if (status === 'pending') { 227 | var args = resultArgs = (arguments.length > 1) ? arguments[1] : []; 228 | for (var i = 0; i < progressFuncs.length; i++) { 229 | progressFuncs[i].apply(context, args); 230 | } 231 | } 232 | return this; 233 | }, 234 | 235 | resolve: function() { 236 | return this.resolveWith(this, arguments); 237 | }, 238 | 239 | reject: function() { 240 | return this.rejectWith(this, arguments); 241 | }, 242 | 243 | notify: function() { 244 | return this.notifyWith(this, arguments); 245 | } 246 | } 247 | 248 | var obj = promise.promise(deferred); 249 | 250 | if (fn) { 251 | fn.apply(obj, [obj]); 252 | } 253 | 254 | return obj; 255 | } 256 | 257 | D.when = function() { 258 | if (arguments.length < 2) { 259 | var obj = arguments.length ? arguments[0] : undefined; 260 | if (obj && (typeof obj.isResolved === 'function' && typeof obj.isRejected === 'function')) { 261 | return obj.promise(); 262 | } 263 | else { 264 | return D().resolve(obj).promise(); 265 | } 266 | } 267 | else { 268 | return (function(args){ 269 | var df = D(), 270 | size = args.length, 271 | done = 0, 272 | 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 273 | 274 | for (var i = 0; i < args.length; i++) { 275 | (function(j) { 276 | var obj = null; 277 | 278 | if (args[j].done) { 279 | args[j].done(function() { rp[j] = (arguments.length < 2) ? arguments[0] : arguments; if (++done == size) { df.resolve.apply(df, rp); }}) 280 | .fail(function() { df.reject(arguments); }); 281 | } else { 282 | obj = args[j]; 283 | args[j] = new Deferred(); 284 | 285 | args[j].done(function() { rp[j] = (arguments.length < 2) ? arguments[0] : arguments; if (++done == size) { df.resolve.apply(df, rp); }}) 286 | .fail(function() { df.reject(arguments); }).resolve(obj); 287 | } 288 | })(i); 289 | } 290 | 291 | return df.promise(); 292 | })(arguments); 293 | } 294 | } 295 | 296 | global.Deferred = D; 297 | })(window); -------------------------------------------------------------------------------- /js/deferred.min.js: -------------------------------------------------------------------------------- 1 | !function(global){function isArray(arr){return"[object Array]"===Object.prototype.toString.call(arr)}function foreach(arr,handler){if(isArray(arr))for(var i=0;i1&&arguments[1]&&this.fail(arguments[1]),arguments.length>0&&arguments[0]&&this.done(arguments[0]),arguments.length>2&&arguments[2]&&this.progress(arguments[2])},promise:function(obj){if(null==obj)return promise;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"rejected"===status},isResolved:function(){return"resolved"===status},pipe:function(done,fail){return D(function(def){foreach(done,function(func){"function"==typeof func?deferred.done(function(){var returnval=func.apply(this,arguments);returnval&&"function"==typeof returnval?returnval.promise().then(def.resolve,def.reject,def.notify):def.resolve(returnval)}):deferred.done(def.resolve)}),foreach(fail,function(func){"function"==typeof func?deferred.fail(function(){var returnval=func.apply(this,arguments);returnval&&"function"==typeof returnval?returnval.promise().then(def.resolve,def.reject,def.notify):def.reject(returnval)}):deferred.fail(def.reject)})}).promise()}},deferred={resolveWith:function(context){if("pending"===status){status="resolved";for(var args=resultArgs=arguments.length>1?arguments[1]:[],i=0;i1?arguments[1]:[],i=0;i1?arguments[1]:[],i=0;i 1 && arguments[1]) { 138 | // this.fail(arguments[1]); 139 | // } 140 | // 141 | // // done callbacks 142 | // if (arguments.length > 0 && arguments[0]) { 143 | // this.done(arguments[0]); 144 | // } 145 | // 146 | // // notify callbacks 147 | // if (arguments.length > 2 && arguments[2]) { 148 | // this.progress(arguments[2]); 149 | // } 150 | // 151 | // return this; 152 | // }, 153 | 154 | promise: function(obj) { 155 | if (obj == null) { 156 | return promise; 157 | } else { 158 | for (var i in promise) { 159 | obj[i] = promise[i]; 160 | } 161 | return obj; 162 | } 163 | }, 164 | 165 | state: function() { 166 | return status; 167 | }, 168 | 169 | debug: function() { 170 | console.log('id', thisId); 171 | console.log('[debug]', doneFuncs, failFuncs, status); 172 | }, 173 | 174 | isRejected: function() { 175 | return status === 'rejected'; 176 | }, 177 | 178 | isResolved: function() { 179 | return status === 'resolved'; 180 | }, 181 | 182 | pipe: function(done, fail, progress) { 183 | var newDef = D(function(def) { 184 | var that = this; 185 | foreach(done || null, function(func) { 186 | // filter function 187 | if (typeof func === 'function') { 188 | deferred.done(function() { 189 | var returnval = func.apply(this, arguments); 190 | // if a new deferred/promise is returned, its state is passed to the current deferred/promise 191 | if (returnval && typeof returnval.promise === 'function') { 192 | returnval.promise().done(def.resolve).fail(def.reject).progress(def.notify); 193 | } 194 | else { // if new return val is passed, it is passed to the piped done 195 | def.resolveWith(this === promise ? def.promise() : this, [returnval]); 196 | } 197 | }.bind(that)); 198 | } else { 199 | deferred.done(def.resolve); 200 | } 201 | }); 202 | 203 | foreach(fail || null, function(func) { 204 | if (typeof func === 'function') { 205 | deferred.fail(function() { 206 | var returnval = func.apply(this, arguments); 207 | 208 | if (returnval && typeof returnval.promise === 'function') { 209 | returnval.promise().done(def.resolve).fail(def.reject).progress(def.notify); 210 | } else { 211 | def.rejectWith(this === promise ? def.promise() : this, [returnval]); 212 | } 213 | }.bind(that)); 214 | } 215 | else { 216 | deferred.fail(def.reject); 217 | } 218 | }); 219 | 220 | foreach(progress || null, function(func) { 221 | if (typeof func === 'function') { 222 | deferred.progress(function() { 223 | var returnval = func.apply(this, arguments); 224 | 225 | if (returnval && typeof returnval.promise === 'function') { 226 | returnval.promise().done(def.resolve).fail(def.reject).progress(def.notify); 227 | } else { 228 | def.notifyWith(this === promise ? def.promise() : this, [returnval]); 229 | } 230 | }.bind(that)); 231 | } 232 | else { 233 | deferred.progress(def.notify); 234 | } 235 | }); 236 | }); 237 | 238 | return newDef.promise(); 239 | }, 240 | 241 | getContext: function() { 242 | return context; 243 | }, 244 | 245 | getId: function() { 246 | return thisId; 247 | } 248 | }, 249 | 250 | deferred = { 251 | resolveWith: function(ctx) { 252 | if (status === 'pending') { 253 | status = 'resolved'; 254 | var args = resultArgs = (arguments.length > 1) ? arguments[1] : []; 255 | for (var i = 0; i < doneFuncs.length; i++) { 256 | doneFuncs[i].apply(ctx, args); 257 | } 258 | } 259 | 260 | // context = ctx; 261 | 262 | return this; 263 | }, 264 | 265 | rejectWith: function(ctx) { 266 | if (status === 'pending') { 267 | status = 'rejected'; 268 | var args = resultArgs = (arguments.length > 1) ? arguments[1] : []; 269 | for (var i = 0; i < failFuncs.length; i++) { 270 | failFuncs[i].apply(ctx, args); 271 | } 272 | } 273 | 274 | // context = ctx; 275 | 276 | return this; 277 | }, 278 | 279 | notifyWith: function(ctx) { 280 | var args; 281 | 282 | if (status === 'pending') { 283 | args = lastNotify = (arguments.length > 1) ? arguments[1] : []; 284 | for (var i = 0; i < progressFuncs.length; i++) { 285 | progressFuncs[i].apply(ctx, args); 286 | } 287 | 288 | // context = ctx; 289 | } 290 | 291 | return this; 292 | }, 293 | 294 | resolve: function() { 295 | var ret = deferred.resolveWith(this === deferred ? promise : this, arguments); 296 | return this !== deferred ? this : ret; 297 | }, 298 | 299 | reject: function() { 300 | var ret = deferred.rejectWith(this === deferred ? promise : this, arguments); 301 | return this !== deferred ? this : ret; 302 | }, 303 | 304 | notify: function() { 305 | var ret = deferred.notifyWith(this === deferred ? promise : this, arguments); 306 | return this !== deferred ? this : ret; 307 | } 308 | } 309 | 310 | promise.then = promise.pipe; 311 | 312 | var obj = promise.promise(deferred); 313 | 314 | context = obj; 315 | 316 | obj.id = deferred.id = thisId; 317 | 318 | if (fn) { 319 | fn.apply(obj, [obj]); 320 | } 321 | 322 | return obj; 323 | }; 324 | 325 | D.when = function() { 326 | if (arguments.length < 2) { 327 | var obj = arguments.length ? arguments[0] : undefined; 328 | if (obj && (typeof obj.isResolved === 'function' && typeof obj.isRejected === 'function')) { 329 | return obj.promise(); 330 | } 331 | else { 332 | return D().resolveWith(window, [obj]).promise(); 333 | } 334 | } 335 | else { 336 | return (function(args){ 337 | var df = D(), 338 | size = args.length, 339 | done = 0, 340 | 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 341 | pp = new Array(size), 342 | whenContext = []; 343 | 344 | for (var i = 0; i < args.length; i++) { 345 | whenContext[i] = args[i] && args[i].promise ? args[i].promise() : undefined; 346 | (function(j) { 347 | var obj = null; 348 | 349 | if (args[j].done) { 350 | args[j].done(function() { rp[j] = (arguments.length < 2) ? arguments[0] : arguments; if (++done == size) { df.resolve.apply(whenContext, rp); }}) 351 | .fail(function() { df.reject.apply(whenContext, arguments); }); 352 | } else { 353 | obj = args[j]; 354 | args[j] = new Deferred(); 355 | 356 | args[j].done(function() { rp[j] = (arguments.length < 2) ? arguments[0] : arguments; if (++done == size) { df.resolve.apply(whenContext, rp); }}) 357 | .fail(function() { df.reject.apply(whenContext, arguments); }).resolve(obj); 358 | } 359 | 360 | args[j].progress(function() { 361 | pp[j] = (arguments.length < 2) ? arguments[0] : arguments; 362 | df.notify.apply(whenContext, pp); 363 | }); 364 | })(i); 365 | } 366 | 367 | return df.promise(); 368 | })(arguments); 369 | } 370 | } 371 | 372 | return D; 373 | })({}); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deferred-js", 3 | "version": "1.0.3", 4 | "description": "Light (less than 1 kb gzipped) standalone implementation of promise/deferred that aims to be fully compatible with $.Deferred found in jQuery 1.5+.", 5 | "main": "node_module/jq-deferred.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/warpdesign/Standalone-Deferred.git" 12 | }, 13 | "keywords": [ 14 | "deferred", 15 | "promise", 16 | "jquery" 17 | ], 18 | "author": "Nicolas Ramz", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/warpdesign/Standalone-Deferred/issues" 22 | } 23 | } 24 | --------------------------------------------------------------------------------