summaryrefslogtreecommitdiff
path: root/js/src/Ice/Promise.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/Ice/Promise.js')
-rw-r--r--js/src/Ice/Promise.js297
1 files changed, 297 insertions, 0 deletions
diff --git a/js/src/Ice/Promise.js b/js/src/Ice/Promise.js
new file mode 100644
index 00000000000..91608d13873
--- /dev/null
+++ b/js/src/Ice/Promise.js
@@ -0,0 +1,297 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+(function(global){
+ require("Ice/Class");
+
+ var Ice = global.Ice || {};
+
+ //
+ // Promise State
+ //
+ var State = {Pending: 0, Success: 1, Failed: 2};
+
+ var resolveImp = function(self, listener)
+ {
+ var callback = self.__state === State.Success ? listener.onResponse : listener.onException;
+ try
+ {
+ if(typeof callback !== "function")
+ {
+ listener.promise.setState(self.__state, self._args);
+ }
+ else
+ {
+ var result = callback.apply(null, self._args);
+
+ //
+ // Callback can return a new promise.
+ //
+ if(result && typeof result.then == "function")
+ {
+ result.then(
+ function()
+ {
+ var args = arguments;
+ listener.promise.succeed.apply(listener.promise, args);
+ },
+ function()
+ {
+ var args = arguments;
+ listener.promise.fail.apply(listener.promise, args);
+ });
+ }
+ else
+ {
+ listener.promise.succeed(result);
+ }
+ }
+ }
+ catch(e)
+ {
+ listener.promise.fail.call(listener.promise, e);
+ }
+ };
+
+ var Promise = Ice.Class({
+ __init__: function()
+ {
+ this.__state = State.Pending;
+ this.__listeners = [];
+ },
+ then: function(onResponse, onException)
+ {
+ var promise = new Promise();
+ var self = this;
+ //
+ // Use setTimeout so the listeners are not resolved until the call stack is empty.
+ //
+ setTimeout(
+ function()
+ {
+ self.__listeners.push(
+ {
+ promise:promise,
+ onResponse:onResponse,
+ onException:onException
+ });
+ self.resolve();
+ }, 0);
+ return promise;
+ },
+ exception: function(onException)
+ {
+ return this.then(null, onException);
+ },
+ finally: function(cb)
+ {
+ var p = new Promise();
+ var self = this;
+
+ var finallyHandler = function(method)
+ {
+ return function()
+ {
+ var args = arguments;
+ try
+ {
+ var result = cb.apply(null, args);
+ if(result && typeof result.then == "function")
+ {
+ var handler = function(){ method.apply(p, args); };
+ result.then(handler).exception(handler);
+ }
+ else
+ {
+ method.apply(p, args);
+ }
+ }
+ catch(e)
+ {
+ method.apply(p, args);
+ }
+ };
+ };
+
+ setTimeout(
+ function(){
+ self.then(finallyHandler(p.succeed), finallyHandler(p.fail));
+ });
+ return p;
+ },
+ delay: function(ms)
+ {
+ var p = new Promise();
+
+ var self = this;
+
+ var delayHandler = function(promise, method)
+ {
+ return function()
+ {
+ var args = arguments;
+ setTimeout(
+ function()
+ {
+ method.apply(promise, args);
+ },
+ ms);
+ };
+ };
+
+ setTimeout(
+ function()
+ {
+ self.then(delayHandler(p, p.succeed),
+ delayHandler(p, p.fail));
+ });
+
+ return p;
+ },
+ resolve: function()
+ {
+ if(this.__state === State.Pending)
+ {
+ return;
+ }
+
+ var obj;
+ while((obj = this.__listeners.pop()))
+ {
+ //
+ // We use a separate function here to capture the listeners
+ // in the loop.
+ //
+ resolveImp(this, obj);
+ }
+ },
+ setState: function(state, args)
+ {
+ if(this.__state === State.Pending && state !== State.Pending)
+ {
+ this.__state = state;
+ this._args = args;
+ //
+ // Use setTimeout so the listeners are not resolved until the call stack is empty.
+ //
+ var self = this;
+ setTimeout(function(){ self.resolve(); }, 0);
+ }
+ },
+ succeed: function()
+ {
+ var args = arguments;
+ this.setState(State.Success, args);
+ return this;
+ },
+ fail: function()
+ {
+ var args = arguments;
+ this.setState(State.Failed, args);
+ return this;
+ },
+ succeeded: function()
+ {
+ return this.__state === State.Success;
+ },
+ failed: function()
+ {
+ return this.__state === State.Failed;
+ },
+ completed: function()
+ {
+ return this.__state !== State.Pending;
+ }
+ });
+
+ //
+ // Create a new promise object that is fulfilled when all the promise arguments
+ // are fulfilled or is rejected when one of the promises is rejected.
+ //
+ Promise.all = function()
+ {
+ // If only one argument is provided, check if the argument is an array
+ if(arguments.length === 1 && arguments[0] instanceof Array)
+ {
+ return Promise.all.apply(this, arguments[0]);
+ }
+
+ var promise = new Promise();
+ var promises = Array.prototype.slice.call(arguments);
+ var results = new Array(arguments.length);
+
+ var pending = promises.length;
+ if(pending === 0)
+ {
+ promise.succeed.apply(promise, results);
+ }
+ for(var i = 0; i < promises.length; ++i)
+ {
+ //
+ // Create an anonymous function to capture the loop index
+ //
+
+ /*jshint -W083 */
+ (function(j)
+ {
+ if(promises[j] && typeof promises[j].then == "function")
+ {
+ promises[j].then(
+ function()
+ {
+ results[j] = arguments;
+ pending--;
+ if(pending === 0)
+ {
+ promise.succeed.apply(promise, results);
+ }
+ },
+ function()
+ {
+ promise.fail.apply(promise, arguments);
+ });
+ }
+ else
+ {
+ results[j] = promises[j];
+ pending--;
+ if(pending === 0)
+ {
+ promise.succeed.apply(promise, results);
+ }
+ }
+ }(i));
+ /*jshint +W083 */
+ }
+ return promise;
+ };
+
+ Promise.try = function(onResponse)
+ {
+ return new Promise().succeed().then(onResponse);
+ };
+
+ Promise.delay = function(ms)
+ {
+ if(arguments.length > 1)
+ {
+ var p = new Promise();
+ var args = Array.prototype.slice.call(arguments);
+ ms = args.pop();
+ return p.succeed.apply(p, args).delay(ms);
+ }
+ else
+ {
+ return new Promise().succeed().delay(ms);
+ }
+ };
+
+ Ice.Promise = Promise;
+ global.Ice = Ice;
+}(typeof (global) === "undefined" ? window : global));