├── README.md ├── fs-promise.js ├── package.json ├── promise.js └── test-promise.js /README.md: -------------------------------------------------------------------------------- 1 | This package is deprecated, as the Node now provides this functionality itself. This package exists only for historical purposes. 2 | 3 | The node-promise project provides a complete promise implementation. Promises provide a clean separation 4 | of concerns between asynchronous behavior and the interface so asynchronous 5 | functions can be called without callbacks, and callback interaction can be 6 | done on the generic promise interface. The node-promise package provides just a promise implementation, however, https://github.com/kriszyp/promised-io is recommended for more complete promise-based IO functionality. The promised-io includes the promise implementation from node-promise, as well as wrappers around Node's filesystem and other system I/O APIs for consistent promise-based interaction. 7 | 8 | The node-promise module features a promise implementation with: 9 | 10 | * Chainable promises 11 | * Promises throw errors if an error handler is not provided 12 | * CommonJS promise proposal [1] compliant 13 | * Immutable once fulfilled to reduce possible side-effects 14 | * Promises can be used securely (as separate resolver/promise pairs in 15 | ocap situations) 16 | * Backwards compatibility where possible (addCallback, addErrback, 17 | emitSuccess, and emitError should still behave as expected) 18 | 19 | Utility functions, including: 20 | 21 | * when() - Normalization of sync (normal values) and async (promises) 22 | * all() - Create a promise that accumulate multiple concurrent promises (failed promises resolve to Error objects) 23 | * allOrNone() - Ditto, but the first promise to fail causes the composition to fail as well 24 | * first() - Find the first promise to be fulfilled in a group of promises 25 | * seq() - Sequentially execute a set of promise returning functions 26 | * delay() - Returns a promise that is fulfilled after a given amount of time 27 | * execute() - Executes a function that takes a callback and returns a 28 | promise (thank you Benjamin Thomas for providing this) 29 | 30 | Much of this is adapted from Tyler Close's ref_send and Kris Kowal's work on promises. 31 | 32 | Some quick examples from test-promise.js (again, it is recommended that you use http://github.com/kriszyp/promised-io for file and other I/O interaction): 33 | util = require('util'); 34 | var fs = require('./fs-promise'); 35 | 36 | // open a file and read it 37 | fs.open("fs-promise.js", process.O_RDONLY).then(function(fd){ 38 | return fs.read(fd, 4096); 39 | }).then(function(args){ 40 | util.puts(args[0]); // print the contents of the file 41 | }); 42 | 43 | // does the same thing 44 | fs.readFile("fs-promise.js").addCallback(util.puts); 45 | 46 | A default Promise constructor can be used to create a self-resolving deferred/promise: 47 | 48 | var Promise = require("promise").Promise; 49 | var promise = new Promise(); 50 | asyncOperation(function(){ 51 | Promise.resolve("successful result"); 52 | }); 53 | promise -> given to the consumer 54 | 55 | A consumer can use the promise: 56 | 57 | promise.then(function(result){ 58 | ... when the action is complete this is executed ... 59 | }, 60 | function(error){ 61 | ... executed when the promise fails 62 | }); 63 | 64 | Alternately, a provider can create a deferred and resolve it when it completes an action. 65 | The deferred object a promise object that provides a separation of consumer and producer to protect 66 | promises from being fulfilled by untrusted code. 67 | 68 | var defer = require("promise").defer; 69 | var deferred = defer(); 70 | asyncOperation(function(){ 71 | deferred.resolve("succesful result"); 72 | }); 73 | deferred.promise -> given to the consumer 74 | 75 | Another way that a consumer can use promises: 76 | 77 | var when = require("promise").when; 78 | when(promise,function(result){ 79 | ... when the action is complete this is executed ... 80 | }, 81 | function(error){ 82 | ... executed when the promise fails 83 | }); 84 | 85 | More examples: 86 | 87 | function printFirstAndList(itemsDeferred){ 88 | findFirst(itemsDeferred).then(util.puts); 89 | findLast(itemsDeferred).then(util.puts); 90 | } 91 | function findFirst(itemsDeferred){ 92 | return itemsDeferred.then(function(items){ 93 | return items[0]; 94 | }); 95 | } 96 | function findLast(itemsDeferred){ 97 | return itemsDeferred.then(function(items){ 98 | return items[items.length]; 99 | }); 100 | } 101 | 102 | And now you can do: 103 | 104 | printFirstAndLast(someAsyncFunction()); 105 | 106 | 107 | The workhorse function of this library is the "when" function, which provides a means for normalizing interaction with values and functions that may be a normal synchronous value, or may be a promise (asynchronously fulfilled). The when() function takes a value that may be a promise or a normal value for the first function, and when the value is ready executes the function provided as the second argument (immediately in the case of a non-promise normal value). The value returned from when() is the result of the execution of the provided function, and returns a promise if provided a promise or synchronously returns a normal value if provided a non-promise value. This makes it easy to "chain" computations together. This allows us to write code that is agnostic to sync/async interfaces: 108 | 109 | var when = require("promise").when; 110 | function printFirstAndLast(items){ 111 | // print the first and last item 112 | when(findFirst(items), util.puts); 113 | when(findLast(items), util.puts); 114 | } 115 | function findFirst(items){ 116 | // return the first item 117 | return when(items, function(items){ 118 | return items[0]; 119 | }); 120 | } 121 | function findLast(items){ 122 | // return the last item 123 | return when(items, function(items){ 124 | return items[items.length - 1]; 125 | }); 126 | } 127 | 128 | Now we can do: 129 | 130 | > printFirstAndLast([1,2,3,4,5]); 131 | 1 132 | 5 133 | 134 | And we can also provide asynchronous promise: 135 | 136 | var promise = new process.Promise(); 137 | > printFirstAndLast(promise); 138 | 139 | (nothing printed yet) 140 | 141 | > promise.emitSuccess([2,4,6,8,10]); 142 | 2 143 | 10 144 | 145 | 146 | The "all" function is intended to provide a means for waiting for the completion of an array of promises. The "all" function should be passed an array of promises, and it returns an promise that is fulfilled once all the promises in the array are fulfilled. The returned promise's resolved value will be an array with the resolved values of all of the promises in the passed in array. 147 | 148 | The "first" function is intended to provide a means for waiting for the completion of the first promise in an array of promises to be fulfilled. The "first" function should be passed an array of promises, and it returns an promise that is fulfilled once the first promise in the array is fulfilled. The returned promise's resolved value will be the resolved value of the first fulfilled promise. 149 | 150 | -------------------------------------------------------------------------------- /fs-promise.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Node fs module that returns promises 3 | */ 4 | 5 | var fs = require("fs"), 6 | convertNodeAsyncFunction = require("./promise").convertNodeAsyncFunction; 7 | 8 | // convert all the non-sync functions 9 | for (var i in fs) { 10 | if (!(i.match(/Sync$/))) { 11 | exports[i] = convertNodeAsyncFunction(fs[i]); 12 | } 13 | } 14 | 15 | // convert the functions that don't have a declared callback 16 | exports.writeFile = convertNodeAsyncFunction(fs.writeFile, true); 17 | exports.readFile = convertNodeAsyncFunction(fs.readFile, true); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-promise", 3 | "description": "Kris Zyp's implementation of promises with added features, maintained as an npm package.", 4 | "version": "0.5.4", 5 | "homepage": "https://github.com/MaxMotovilov/node-promise", 6 | "files": [ "package.json", "promise.js", "fs-promise.js" ], 7 | "main": "promise.js", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://MaxMotovilov@github.com/MaxMotovilov/node-promise.git" 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /promise.js: -------------------------------------------------------------------------------- 1 | 2 | // Kris Zyp 3 | // Updates/added features by ...Max... (Max Motovilov) 4 | 5 | // this is based on the CommonJS spec for promises: 6 | // http://wiki.commonjs.org/wiki/Promises 7 | // Includes convenience functions for promises, much of this is taken from Tyler Close's ref_send 8 | // and Kris Kowal's work on promises. 9 | // // MIT License 10 | 11 | // A typical usage: 12 | // A default Promise constructor can be used to create a self-resolving deferred/promise: 13 | // var Promise = require("promise").Promise; 14 | // var promise = new Promise(); 15 | // asyncOperation(function(){ 16 | // Promise.resolve("succesful result"); 17 | // }); 18 | // promise -> given to the consumer 19 | // 20 | // A consumer can use the promise 21 | // promise.then(function(result){ 22 | // ... when the action is complete this is executed ... 23 | // }, 24 | // function(error){ 25 | // ... executed when the promise fails 26 | // }); 27 | // 28 | // Alternately, a provider can create a deferred and resolve it when it completes an action. 29 | // The deferred object a promise object that provides a separation of consumer and producer to protect 30 | // promises from being fulfilled by untrusted code. 31 | // var defer = require("promise").defer; 32 | // var deferred = defer(); 33 | // asyncOperation(function(){ 34 | // deferred.resolve("succesful result"); 35 | // }); 36 | // deferred.promise -> given to the consumer 37 | // 38 | // Another way that a consumer can use the promise (using promise.then is also allowed) 39 | // var when = require("promise").when; 40 | // when(promise,function(result){ 41 | // ... when the action is complete this is executed ... 42 | // }, 43 | // function(error){ 44 | // ... executed when the promise fails 45 | // }); 46 | try{ 47 | var enqueue = require("event-queue").enqueue; 48 | } 49 | catch(e){ 50 | // squelch the error, and only complain if the queue is needed 51 | } 52 | if(!enqueue){ 53 | enqueue = (typeof process !== "undefined" && process.nextTick) || function(func){ 54 | func(); 55 | } 56 | } 57 | // commented out due to: http://code.google.com/p/v8/issues/detail?id=851 58 | var freeze = /*Object.freeze || */function(){}; 59 | 60 | /** 61 | * Default constructor that creates a self-resolving Promise. Not all promise implementations 62 | * need to use this constructor. 63 | */ 64 | var Promise = function(canceller){ 65 | }; 66 | 67 | /** 68 | * Promise implementations must provide a "then" function. 69 | */ 70 | Promise.prototype.then = function(resolvedCallback, errorCallback, progressCallback){ 71 | throw new TypeError("The Promise base class is abstract, this function must be implemented by the Promise implementation"); 72 | }; 73 | 74 | /** 75 | * If an implementation of a promise supports a concurrency model that allows 76 | * execution to block until the promise is resolved, the wait function may be 77 | * added. 78 | */ 79 | /** 80 | * If an implementation of a promise can be cancelled, it may add this function 81 | */ 82 | // Promise.prototype.cancel = function(){ 83 | // }; 84 | 85 | Promise.prototype.get = function(propertyName){ 86 | return this.then(function(value){ 87 | return value[propertyName]; 88 | }); 89 | }; 90 | 91 | Promise.prototype.put = function(propertyName, value){ 92 | return this.then(function(object){ 93 | return object[propertyName] = value; 94 | }); 95 | }; 96 | 97 | Promise.prototype.call = function(functionName /*, args */){ 98 | return this.then(function(value){ 99 | return value[functionName].apply(value, Array.prototype.slice.call(arguments, 1)); 100 | }); 101 | }; 102 | 103 | /** Dojo/NodeJS methods*/ 104 | Promise.prototype.addCallback = function(callback){ 105 | return this.then(callback); 106 | }; 107 | 108 | Promise.prototype.addErrback = function(errback){ 109 | return this.then(function(){}, errback); 110 | }; 111 | 112 | /*Dojo methods*/ 113 | Promise.prototype.addBoth = function(callback){ 114 | return this.then(callback, callback); 115 | }; 116 | 117 | Promise.prototype.addCallbacks = function(callback, errback){ 118 | return this.then(callback, errback); 119 | }; 120 | 121 | /*NodeJS method*/ 122 | Promise.prototype.wait = function(){ 123 | return exports.wait(this); 124 | }; 125 | 126 | /** 127 | * When promise is resolved or rejected, call a single callback in the style of Node callbacks 128 | * 129 | * @param {Function} nodeCallback The callback function (e.g. function(err, result)) 130 | * @param {*=} opt_scope Optional scope to set in the callback (default: null) 131 | * @return {*} 132 | */ 133 | Promise.prototype.thenNode = function(nodeCallback, opt_scope) { 134 | return this.then( 135 | nodeCallback.bind(opt_scope ? opt_scope : null, null), // no err 136 | nodeCallback.bind(opt_scope ? opt_scope : null) // err is first param 137 | ); 138 | }; 139 | 140 | Deferred.prototype = Promise.prototype; 141 | // A deferred provides an API for creating and resolving a promise. 142 | exports.Promise = exports.Deferred = exports.defer = defer; 143 | function defer(){ 144 | return new Deferred(); 145 | } 146 | 147 | var contextHandler = exports.contextHandler = {}; 148 | 149 | function Deferred(canceller){ 150 | var result, finished, isError, waiting = [], handled; 151 | var promise = this.promise = new Promise(); 152 | var currentContextHandler = contextHandler.getHandler && contextHandler.getHandler(); 153 | 154 | function notifyAll(value){ 155 | if(finished){ 156 | throw new Error("This deferred has already been resolved"); 157 | } 158 | result = value; 159 | finished = true; 160 | for(var i = 0; i < waiting.length; i++){ 161 | notify(waiting[i]); 162 | } 163 | } 164 | function notify(listener){ 165 | var func = (isError ? listener.error : listener.resolved); 166 | if(func){ 167 | handled = true; 168 | enqueue(function(){ 169 | if(currentContextHandler){ 170 | currentContextHandler.resume(); 171 | } 172 | try{ 173 | var newResult = func(result); 174 | if(newResult && typeof newResult.then === "function"){ 175 | newResult.then(listener.deferred.resolve, listener.deferred.reject); 176 | return; 177 | } 178 | listener.deferred.resolve(newResult); 179 | } 180 | catch(e){ 181 | listener.deferred.reject(e); 182 | } 183 | finally{ 184 | if(currentContextHandler){ 185 | currentContextHandler.suspend(); 186 | } 187 | } 188 | }); 189 | } 190 | else{ 191 | if(isError){ 192 | if (listener.deferred.reject(result, true)) { 193 | handled = true; 194 | } 195 | } 196 | else{ 197 | listener.deferred.resolve.apply(listener.deferred, result); 198 | } 199 | } 200 | } 201 | // calling resolve will resolve the promise 202 | this.resolve = this.callback = this.emitSuccess = function(value){ 203 | notifyAll(value); 204 | }; 205 | 206 | // calling error will indicate that the promise failed 207 | var reject = this.reject = this.errback = this.emitError = function(error, dontThrow){ 208 | isError = true; 209 | notifyAll(error); 210 | if (!dontThrow) { 211 | enqueue(function () { 212 | if (!handled) { 213 | throw error; 214 | } 215 | }); 216 | } 217 | return handled; 218 | }; 219 | 220 | // call progress to provide updates on the progress on the completion of the promise 221 | this.progress = function(update){ 222 | for(var i = 0; i < waiting.length; i++){ 223 | var progress = waiting[i].progress; 224 | progress && progress(update); 225 | } 226 | } 227 | // provide the implementation of the promise 228 | this.then = promise.then = function(resolvedCallback, errorCallback, progressCallback){ 229 | var returnDeferred = new Deferred(promise.cancel); 230 | var listener = {resolved: resolvedCallback, error: errorCallback, progress: progressCallback, deferred: returnDeferred}; 231 | if(finished){ 232 | notify(listener); 233 | } 234 | else{ 235 | waiting.push(listener); 236 | } 237 | return returnDeferred.promise; 238 | }; 239 | var timeout; 240 | if(typeof setTimeout !== "undefined") { 241 | this.timeout = function (ms) { 242 | if (ms === undefined) { 243 | return timeout; 244 | } 245 | timeout = ms; 246 | setTimeout(function () { 247 | if (!finished) { 248 | if (promise.cancel) { 249 | promise.cancel(new Error("timeout")); 250 | } 251 | else { 252 | reject(new Error("timeout")); 253 | } 254 | } 255 | }, ms); 256 | return promise; 257 | }; 258 | } 259 | 260 | if(canceller){ 261 | this.cancel = promise.cancel = function(){ 262 | var error = canceller(); 263 | if(!(error instanceof Error)){ 264 | error = new Error(error); 265 | } 266 | reject(error); 267 | } 268 | } 269 | freeze(promise); 270 | }; 271 | 272 | function perform(value, async, sync){ 273 | try{ 274 | if(value && typeof value.then === "function"){ 275 | value = async(value); 276 | } 277 | else{ 278 | value = sync(value); 279 | } 280 | if(value && typeof value.then === "function"){ 281 | return value; 282 | } 283 | var deferred = new Deferred(); 284 | deferred.resolve(value); 285 | return deferred.promise; 286 | }catch(e){ 287 | var deferred = new Deferred(); 288 | deferred.reject(e); 289 | return deferred.promise; 290 | } 291 | 292 | } 293 | /** 294 | * Promise manager to make it easier to consume promises 295 | */ 296 | 297 | /** 298 | * Registers an observer on a promise. 299 | * @param value promise or value to observe 300 | * @param resolvedCallback function to be called with the resolved value 301 | * @param rejectCallback function to be called with the rejection reason 302 | * @param progressCallback function to be called when progress is made 303 | * @return promise for the return value from the invoked callback 304 | */ 305 | exports.whenPromise = function(value, resolvedCallback, rejectCallback, progressCallback){ 306 | return perform(value, function(value){ 307 | return value.then(resolvedCallback, rejectCallback, progressCallback); 308 | }, 309 | function(value){ 310 | return resolvedCallback(value); 311 | }); 312 | }; 313 | /** 314 | * Registers an observer on a promise. 315 | * @param value promise or value to observe 316 | * @param resolvedCallback function to be called with the resolved value 317 | * @param rejectCallback function to be called with the rejection reason 318 | * @param progressCallback function to be called when progress is made 319 | * @return promise for the return value from the invoked callback or the value if it 320 | * is a non-promise value 321 | */ 322 | exports.when = function(value, resolvedCallback, rejectCallback, progressCallback){ 323 | if(value && typeof value.then === "function"){ 324 | return exports.whenPromise(value, resolvedCallback, rejectCallback, progressCallback); 325 | } 326 | return resolvedCallback(value); 327 | }; 328 | 329 | /** 330 | * Gets the value of a property in a future turn. 331 | * @param target promise or value for target object 332 | * @param property name of property to get 333 | * @return promise for the property value 334 | */ 335 | exports.get = function(target, property){ 336 | return perform(target, function(target){ 337 | return target.get(property); 338 | }, 339 | function(target){ 340 | return target[property] 341 | }); 342 | }; 343 | 344 | /** 345 | * Invokes a method in a future turn. 346 | * @param target promise or value for target object 347 | * @param methodName name of method to invoke 348 | * @param args array of invocation arguments 349 | * @return promise for the return value 350 | */ 351 | exports.post = function(target, methodName, args){ 352 | return perform(target, function(target){ 353 | return target.call(property, args); 354 | }, 355 | function(target){ 356 | return target[methodName].apply(target, args); 357 | }); 358 | }; 359 | 360 | /** 361 | * Sets the value of a property in a future turn. 362 | * @param target promise or value for target object 363 | * @param property name of property to set 364 | * @param value new value of property 365 | * @return promise for the return value 366 | */ 367 | exports.put = function(target, property, value){ 368 | return perform(target, function(target){ 369 | return target.put(property, value); 370 | }, 371 | function(target){ 372 | return target[property] = value; 373 | }); 374 | }; 375 | 376 | 377 | /** 378 | * Waits for the given promise to finish, blocking (and executing other events) 379 | * if necessary to wait for the promise to finish. If target is not a promise 380 | * it will return the target immediately. If the promise results in an reject, 381 | * that reject will be thrown. 382 | * @param target promise or value to wait for. 383 | * @return the value of the promise; 384 | */ 385 | exports.wait = function(target){ 386 | if(!queue){ 387 | throw new Error("Can not wait, the event-queue module is not available"); 388 | } 389 | if(target && typeof target.then === "function"){ 390 | var isFinished, isError, result; 391 | target.then(function(value){ 392 | isFinished = true; 393 | result = value; 394 | }, 395 | function(error){ 396 | isFinished = true; 397 | isError = true; 398 | result = error; 399 | }); 400 | while(!isFinished){ 401 | queue.processNextEvent(true); 402 | } 403 | if(isError){ 404 | throw result; 405 | } 406 | return result; 407 | } 408 | else{ 409 | return target; 410 | } 411 | }; 412 | 413 | 414 | 415 | /** 416 | * Takes an array of promises and returns a promise that is fulfilled once all 417 | * the promises in the array are fulfilled 418 | * @param array The array of promises 419 | * @return the promise that is fulfilled when all the array is fulfilled, resolved to the array of results 420 | */ 421 | 422 | function composeAll(fail_on_error) { 423 | return function(array) { 424 | var deferred = new Deferred(), 425 | once = true; 426 | 427 | if( !(array instanceof Array) ) 428 | array = Array.prototype.slice.call(arguments); 429 | else 430 | array = array.slice(); 431 | var todo = array.reduce( function(count,p){ return count+(p&&p.then?1:0); }, 0 ); 432 | if( todo === 0 ) 433 | deferred.resolve(array); 434 | else 435 | array.forEach( function( p, i ) { 436 | if( p && p.then ) 437 | exports.when( p, succeed, fail_on_error ? failOnce : succeed ); 438 | 439 | function succeed( v ) { 440 | array[i] = v; 441 | if( --todo === 0 ) 442 | deferred.resolve(array); 443 | } 444 | } ); 445 | 446 | function failOnce( err ) { 447 | if( once ) { 448 | array.forEach( function(p) { 449 | if( p.then && p.cancel ) p.cancel(); 450 | } ); 451 | deferred.reject( err ); 452 | once = false; 453 | } 454 | } 455 | 456 | return deferred.promise; 457 | } 458 | } 459 | 460 | exports.all = composeAll(false); 461 | 462 | /** 463 | * Variation of all() -- fails if any of the promises fail 464 | */ 465 | exports.allOrNone = composeAll(true); 466 | 467 | /** 468 | * Takes an array of promises and returns a promise that is fulfilled when the first 469 | * promise in the array of promises is fulfilled 470 | * @param array The array of promises 471 | * @return a promise that is fulfilled with the value of the value of first promise to be fulfilled 472 | */ 473 | exports.first = function(array){ 474 | var deferred = new Deferred(); 475 | if(!(array instanceof Array)){ 476 | array = Array.prototype.slice.call(arguments); 477 | } 478 | var fulfilled; 479 | array.forEach(function(promise, index){ 480 | exports.when(promise, function(value){ 481 | if (!fulfilled) { 482 | fulfilled = true; 483 | deferred.resolve(value); 484 | } 485 | }, 486 | function(error){ 487 | if (!fulfilled) { 488 | fulfilled = true; 489 | deferred.resolve(error); 490 | } 491 | }); 492 | }); 493 | return deferred.promise; 494 | }; 495 | 496 | /** 497 | * Takes an array of asynchronous functions (that return promises) and 498 | * executes them sequentially. Each funtion is called with the return value of the last function 499 | * @param array The array of function 500 | * @param startingValue The value to pass to the first function 501 | * @return the value returned from the last function 502 | */ 503 | exports.seq = function(array, startingValue){ 504 | array = array.concat(); // make a copy 505 | var deferred = new Deferred(); 506 | function next(value){ 507 | var nextAction = array.shift(); 508 | if(nextAction){ 509 | exports.when(nextAction(value), next, deferred.reject); 510 | } 511 | else { 512 | deferred.resolve(value); 513 | } 514 | } 515 | next(startingValue); 516 | return deferred.promise; 517 | }; 518 | 519 | 520 | /** 521 | * Delays for a given amount of time and then fulfills the returned promise. 522 | * @param milliseconds The number of milliseconds to delay 523 | * @return A promise that will be fulfilled after the delay 524 | */ 525 | if(typeof setTimeout !== "undefined") { 526 | exports.delay = function(milliseconds) { 527 | var deferred = new Deferred(); 528 | setTimeout(function(){ 529 | deferred.resolve(); 530 | }, milliseconds); 531 | return deferred.promise; 532 | }; 533 | } 534 | 535 | 536 | 537 | /** 538 | * Runs a function that takes a callback, but returns a Promise instead. 539 | * @param func node compatible async function which takes a callback as its last argument 540 | * @return promise for the return value from the callback from the function 541 | */ 542 | exports.execute = function(asyncFunction){ 543 | var args = Array.prototype.slice.call(arguments, 1); 544 | 545 | var deferred = new Deferred(); 546 | args.push(function(error, result){ 547 | if(error) { 548 | deferred.emitError(error); 549 | } 550 | else { 551 | if(arguments.length > 2){ 552 | // if there are multiple success values, we return an array 553 | Array.prototype.shift.call(arguments, 1); 554 | deferred.emitSuccess(arguments); 555 | } 556 | else{ 557 | deferred.emitSuccess(result); 558 | } 559 | } 560 | }); 561 | asyncFunction.apply(this, args); 562 | return deferred.promise; 563 | }; 564 | 565 | /** 566 | * Converts a Node async function to a promise returning function 567 | * @param func node compatible async function which takes a callback as its last argument 568 | * @return A function that returns a promise 569 | */ 570 | exports.convertNodeAsyncFunction = function(asyncFunction, callbackNotDeclared){ 571 | var arity = asyncFunction.length; 572 | if(callbackNotDeclared){ 573 | arity++; 574 | } 575 | return function(){ 576 | var deferred = new Deferred(); 577 | arguments.length = arity; 578 | arguments[arity - 1] = function(error, result){ 579 | if(error) { 580 | deferred.emitError(error); 581 | } 582 | else { 583 | if(arguments.length > 2){ 584 | // if there are multiple success values, we return an array 585 | Array.prototype.shift.call(arguments, 1); 586 | deferred.emitSuccess(arguments); 587 | } 588 | else{ 589 | deferred.emitSuccess(result); 590 | } 591 | } 592 | }; 593 | asyncFunction.apply(this, arguments); 594 | return deferred.promise; 595 | }; 596 | }; 597 | -------------------------------------------------------------------------------- /test-promise.js: -------------------------------------------------------------------------------- 1 | sys = require("sys"); 2 | var fs = require('./fs-promise'); 3 | 4 | // open a file and read it 5 | fs.open("fs-promise.js", 'r').then(function(fd){ 6 | return fs.read(fd, 4096); 7 | }).then(function(args){ 8 | sys.puts(args[0]); 9 | }); 10 | 11 | // does the same thing 12 | fs.readFile("fs-promise.js").addCallback(sys.puts); 13 | 14 | // does the same thing 15 | fs.readFile("fs-promise.js").thenNode(function(err, result) { 16 | if (err) { 17 | console.error(err); 18 | } else { 19 | console.info('thenNode result success'); 20 | } 21 | }); 22 | 23 | // forced error 24 | fs.readFile("foobar.js").thenNode(function(err, result) { 25 | if (err) { 26 | console.info('thenNode err success'); 27 | } else { 28 | console.error('err should be passed'); 29 | } 30 | }); --------------------------------------------------------------------------------