/** * Ext.space.Promise an Asynchronous API based on the Promises A+ spec http://promisesaplus.com * * Promises are used extensively by Sencha Web Application Client's APIs. Most APIs * return promises. In the case of Ext.space.Invoke, the called application needs to * create, return, and resolve promises. * * To understand how promises work, here is a simple promise-based version of `setTimeout`: * * function wait(time) { * var promise = new Ext.space.Promise(); * * setTimeout(function(){ * promise.fulfill({resolved: new Date().getTime()}); * }, time); * * return promise; * } * * First create the promise, then return the promise so that the caller can react to * the result of the promise. * * The promise can later be resolved when the data is available: * * promise.fulfill(response); * * If an error occurs, call reject on the promise: * * promise.reject(errorMessage); * * * Now your code can call wait instead of setTimeout: * * wait(1000).then(success, failure); * * var success = function(result) { * // Do something with the result * Ext.space.Logger.log("resolved at" + result.resolved); * }; * * var failure = function(error) { * Ext.space.Logger.error('Something went wrong', error); * } */Ext.define('Ext.space.Promise', { statics: { when: function() { var ret = new this, promises = Array.prototype.slice.call(arguments), index = -1, results = [], promise; function onRejected(e) { ret.reject(e); } /** * @method onFulfilled * @param {*} [result] */ function onFulfilled(result) { promise = promises.shift(); if (index >= 0) { results[index] = result; } index++; if (promise) { promise.then(onFulfilled, onRejected); } else { ret.fulfill.apply(ret, results); } } onFulfilled(); return ret; }, whenComplete: function(promises) { var ret = new this, index = -1, fulfilledResults = [], rejectedReasons = [], promise; function onRejected(reason) { promise = promises.shift(); rejectedReasons.push(reason); next(promise); } /** * @method onFulfilled * @param [result] */ function onFulfilled(result) { promise = promises.shift(); fulfilledResults.push(arguments); next(promise); } function next(promise) { index++; if (promise) { promise.then(onFulfilled, onRejected); } else { ret.fulfill.call(ret, { fulfilled: fulfilledResults, rejected: rejectedReasons }); } } next(promises.shift()); return ret; }, from: function() { var promise = new this; promise.completed = 1; promise.lastResults = arguments; return promise; }, fail: function(reason) { var promise = new this; promise.completed = -1; promise.lastReason = reason; return promise; } }, completed: 0, getListeners: function(init) { var listeners = this.listeners; if (!listeners && init) { this.listeners = listeners = []; } return listeners; }, then: function(success, error) { var Promise = Ext.space.Promise, completed = this.completed, promise, result; if (completed === -1) { if (error) { error(this.lastReason); } return this; } if (completed === 1 && !this.isFulfilling) { if (!success) { return this; } result = success.apply(null, this.lastResults); if (result instanceof Promise) { promise = result; } else { promise = Promise.from(result); } } else { promise = new Promise; promise.$owner = this; this.getListeners(true).push({ success: success, error: error, promise: promise }); } return promise; }, error: function(error) { return this.then(null, error); }, /** * * @param {Object...} results * @returns {Ext.space.Promise} */ fulfill: function() { var results = arguments, listeners, listener, success, promise, callbackResults; this.lastResults = results; this.completed = 1; while (listeners = this.getListeners()) { delete this.listeners; this.isFulfilling = true; while (listener = listeners.shift()) { success = listener.success; if (success) { promise = listener.promise; delete promise.$owner; callbackResults = success.apply(null, results); if (callbackResults instanceof Ext.space.Promise) { callbackResults.connect(promise); } else { promise.fulfill(callbackResults); } } } this.isFulfilling = false; } return this; }, connect: function(promise) { this.then(function(result) { promise.fulfill(result); return result; }, promise.reject.bind(promise)); }, /** * * @param {Object} reason * @returns {Ext.space.Promise} */ reject: function(reason) { var listeners = this.getListeners(), listener, error, promise; this.lastReason = reason; this.completed = -1; if (listeners) { delete this.listeners; while (listener = listeners.shift()) { error = listener.error; promise = listener.promise; delete promise.$owner; if (error) { error(reason); } promise.reject(reason); } } return this; }, cancel: function() { var listeners = this.getListeners(), owner = this.$owner, i, ln, listener; if (listeners) { for (i = 0, ln = listeners.length; i < ln; i++) { listener = listeners[i]; listener.promise.cancel(); } listeners.length = 0; delete this.listeners; } if (owner) { delete this.$owner; owner.cancel(); } }}); // if we're not in an ExtJS 6+ environment, alias for backwards compatibility with // our original promise implementation if (typeof Ext.Promise == "undefined") { Ext.Promise = Ext.space.Promise;}