| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553 | // Copyright 2012 The Closure Library Authors. All Rights Reserved.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at////      http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS-IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License./** * @fileoverview This file provides primitives and tools (wait, transform, *     chain, combine) that make it easier to work with Results. This section *     gives an overview of their functionality along with some examples and the *     actual definitions have detailed descriptions next to them. * * * NOTE: goog.result is soft deprecated - we expect to replace this and * goog.async.Deferred with a wrapper around W3C Promises: * http://dom.spec.whatwg.org/#promises. */goog.provide('goog.result');goog.require('goog.array');goog.require('goog.result.DependentResult');goog.require('goog.result.Result');goog.require('goog.result.SimpleResult');/** * Returns a successful result containing the provided value. * * Example: * <pre> * * var value = 'some-value'; * var result = goog.result.immediateResult(value); * assertEquals(goog.result.Result.State.SUCCESS, result.getState()); * assertEquals(value, result.getValue()); * * </pre> * * @param {*} value The value of the result. * @return {!goog.result.Result} A Result object that has already been resolved *     to the supplied value. */goog.result.successfulResult = function(value) {  var result = new goog.result.SimpleResult();  result.setValue(value);  return result;};/** * Returns a failed result with the optional error slug set. * * Example: * <pre> * * var error = new Error('something-failed'); * var result = goog.result.failedResult(error); * assertEquals(goog.result.Result.State.ERROR, result.getState()); * assertEquals(error, result.getError()); * * </pre> * * @param {*=} opt_error The error to which the result should resolve. * @return {!goog.result.Result} A Result object that has already been resolved *     to the supplied Error. */goog.result.failedResult = function(opt_error) {  var result = new goog.result.SimpleResult();  result.setError(opt_error);  return result;};/** * Returns a canceled result. * The result will be resolved to an error of type CancelError. * * Example: * <pre> * * var result = goog.result.canceledResult(); * assertEquals(goog.result.Result.State.ERROR, result.getState()); * var error = result.getError(); * assertTrue(error instanceof goog.result.Result.CancelError); * * </pre> * * @return {!goog.result.Result} A canceled Result. */goog.result.canceledResult = function() {  var result = new goog.result.SimpleResult();  result.cancel();  return result;};/** * Calls the handler on resolution of the result (success or failure). * The handler is passed the result object as the only parameter. The call will * be immediate if the result is no longer pending. * * Example: * <pre> * * var result = xhr.get('testdata/xhr_test_text.data'); * * // Wait for the result to be resolved and alert it's state. * goog.result.wait(result, function(result) { *   alert('State: ' + result.getState()); * }); * </pre> * * @param {!goog.result.Result} result The result to install the handlers. * @param {function(this:T, !goog.result.Result)} handler The handler to be *     called. The handler is passed the result object as the only parameter. * @param {T=} opt_scope Optional scope for the handler. * @template T */goog.result.wait = function(result, handler, opt_scope) {  result.wait(handler, opt_scope);};/** * Calls the handler if the result succeeds. The result object is the only * parameter passed to the handler. The call will be immediate if the result * has already succeeded. * * Example: * <pre> * * var result = xhr.get('testdata/xhr_test_text.data'); * * // attach a success handler. * goog.result.waitOnSuccess(result, function(resultValue, result) { *   var datavalue = result.getvalue(); *   alert('value: ' + datavalue + ' == ' + resultValue); * }); * </pre> * * @param {!goog.result.Result} result The result to install the handlers. * @param {function(this:T, ?, !goog.result.Result)} handler The handler to be *     called. The handler is passed the result value and the result as *     parameters. * @param {T=} opt_scope Optional scope for the handler. * @template T */goog.result.waitOnSuccess = function(result, handler, opt_scope) {  goog.result.wait(result, function(res) {    if (res.getState() == goog.result.Result.State.SUCCESS) {      // 'this' refers to opt_scope      handler.call(this, res.getValue(), res);    }  }, opt_scope);};/** * Calls the handler if the result action errors. The result object is passed as * the only parameter to the handler. The call will be immediate if the result * object has already resolved to an error. * * Example: * * <pre> * * var result = xhr.get('testdata/xhr_test_text.data'); * * // Attach a failure handler. * goog.result.waitOnError(result, function(error) { *  // Failed asynchronous call! * }); * </pre> * * @param {!goog.result.Result} result The result to install the handlers. * @param {function(this:T, ?, !goog.result.Result)} handler The handler to be *     called. The handler is passed the error and the result object as *     parameters. * @param {T=} opt_scope Optional scope for the handler. * @template T */goog.result.waitOnError = function(result, handler, opt_scope) {  goog.result.wait(result, function(res) {    if (res.getState() == goog.result.Result.State.ERROR) {      // 'this' refers to opt_scope      handler.call(this, res.getError(), res);    }  }, opt_scope);};/** * Given a result and a transform function, returns a new result whose value, * on success, will be the value of the given result after having been passed * through the transform function. * * If the given result is an error, the returned result is also an error and the * transform will not be called. * * Example: * <pre> * * var result = xhr.getJson('testdata/xhr_test_json.data'); * * // Transform contents of returned data using 'processJson' and create a * // transformed result to use returned JSON. * var transformedResult = goog.result.transform(result, processJson); * * // Attach success and failure handlers to the transformed result. * goog.result.waitOnSuccess(transformedResult, function(resultValue, result) { *   var jsonData = resultValue; *   assertEquals('ok', jsonData['stat']); * }); * * goog.result.waitOnError(transformedResult, function(error) { *   // Failed getJson call * }); * </pre> * * @param {!goog.result.Result} result The result whose value will be *     transformed. * @param {function(?):?} transformer The transformer *     function. The return value of this function will become the value of the *     returned result. * * @return {!goog.result.DependentResult} A new Result whose eventual value will *     be the returned value of the transformer function. */goog.result.transform = function(result, transformer) {  var returnedResult = new goog.result.DependentResultImpl_([result]);  goog.result.wait(result, function(res) {    if (res.getState() == goog.result.Result.State.SUCCESS) {      returnedResult.setValue(transformer(res.getValue()));    } else {      returnedResult.setError(res.getError());    }  });  return returnedResult;};/** * The chain function aids in chaining of asynchronous Results. This provides a * convenience for use cases where asynchronous operations must happen serially * i.e. subsequent asynchronous operations are dependent on data returned by * prior asynchronous operations. * * It accepts a result and an action callback as arguments and returns a * result. The action callback is called when the first result succeeds and is * supposed to return a second result. The returned result is resolved when one * of both of the results resolve (depending on their success or failure.) The * state and value of the returned result in the various cases is documented * below: * <pre> * * First Result State:    Second Result State:    Returned Result State: * SUCCESS                SUCCESS                 SUCCESS * SUCCESS                ERROR                   ERROR * ERROR                  Not created             ERROR * </pre> * * The value of the returned result, in the case both results succeed, is the * value of the second result (the result returned by the action callback.) * * Example: * <pre> * * var testDataResult = xhr.get('testdata/xhr_test_text.data'); * * // Chain this result to perform another asynchronous operation when this * // Result is resolved. * var chainedResult = goog.result.chain(testDataResult, *     function(testDataResult) { * *       // The result value of testDataResult is the URL for JSON data. *       var jsonDataUrl = testDataResult.getValue(); * *       // Create a new Result object when the original result is resolved. *       var jsonResult = xhr.getJson(jsonDataUrl); * *       // Return the newly created Result. *       return jsonResult; *     }); * * // The chained result resolves to success when both results resolve to * // success. * goog.result.waitOnSuccess(chainedResult, function(resultValue, result) { * *   // At this point, both results have succeeded and we can use the JSON *   // data returned by the second asynchronous call. *   var jsonData = resultValue; *   assertEquals('ok', jsonData['stat']); * }); * * // Attach the error handler to be called when either Result fails. * goog.result.waitOnError(chainedResult, function(result) { *   alert('chained result failed!'); * }); * </pre> * * @param {!goog.result.Result} result The result to chain. * @param {function(this:T, !goog.result.Result):!goog.result.Result} *     actionCallback The callback called when the result is resolved. This *     callback must return a Result. * @param {T=} opt_scope Optional scope for the action callback. * @return {!goog.result.DependentResult} A result that is resolved when both *     the given Result and the Result returned by the actionCallback have *     resolved. * @template T */goog.result.chain = function(result, actionCallback, opt_scope) {  var dependentResult = new goog.result.DependentResultImpl_([result]);  // Wait for the first action.  goog.result.wait(result, function(result) {    if (result.getState() == goog.result.Result.State.SUCCESS) {      // The first action succeeded. Chain the contingent action.      var contingentResult = actionCallback.call(opt_scope, result);      dependentResult.addParentResult(contingentResult);      goog.result.wait(contingentResult, function(contingentResult) {        // The contingent action completed. Set the dependent result based on        // the contingent action's outcome.        if (contingentResult.getState() == goog.result.Result.State.SUCCESS) {          dependentResult.setValue(contingentResult.getValue());        } else {          dependentResult.setError(contingentResult.getError());        }      });    } else {      // First action failed, the dependent result should also fail.      dependentResult.setError(result.getError());    }  });  return dependentResult;};/** * Returns a result that waits on all given results to resolve. Once all have * resolved, the returned result will succeed (and never error). * * Example: * <pre> * * var result1 = xhr.get('testdata/xhr_test_text.data'); * * // Get a second independent Result. * var result2 = xhr.getJson('testdata/xhr_test_json.data'); * * // Create a Result that resolves when both prior results resolve. * var combinedResult = goog.result.combine(result1, result2); * * // Process data after resolution of both results. * goog.result.waitOnSuccess(combinedResult, function(results) { *   goog.array.forEach(results, function(result) { *       alert(result.getState()); *   }); * }); * </pre> * * @param {...!goog.result.Result} var_args The results to wait on. * * @return {!goog.result.DependentResult} A new Result whose eventual value will *     be the resolved given Result objects. */goog.result.combine = function(var_args) {  /** @type {!Array<!goog.result.Result>} */  var results = goog.array.clone(arguments);  var combinedResult = new goog.result.DependentResultImpl_(results);  var isResolved = function(res) {    return res.getState() != goog.result.Result.State.PENDING;  };  var checkResults = function() {    if (combinedResult.getState() == goog.result.Result.State.PENDING &&        goog.array.every(results, isResolved)) {      combinedResult.setValue(results);    }  };  goog.array.forEach(      results, function(result) { goog.result.wait(result, checkResults); });  return combinedResult;};/** * Returns a result that waits on all given results to resolve. Once all have * resolved, the returned result will succeed if and only if all given results * succeeded. Otherwise it will error. * * Example: * <pre> * * var result1 = xhr.get('testdata/xhr_test_text.data'); * * // Get a second independent Result. * var result2 = xhr.getJson('testdata/xhr_test_json.data'); * * // Create a Result that resolves when both prior results resolve. * var combinedResult = goog.result.combineOnSuccess(result1, result2); * * // Process data after successful resolution of both results. * goog.result.waitOnSuccess(combinedResult, function(results) { *   var textData = results[0].getValue(); *   var jsonData = results[1].getValue(); *   assertEquals('Just some data.', textData); *   assertEquals('ok', jsonData['stat']); * }); * * // Handle errors when either or both results failed. * goog.result.waitOnError(combinedResult, function(combined) { *   var results = combined.getError(); * *   if (results[0].getState() == goog.result.Result.State.ERROR) { *     alert('result1 failed'); *   } * *   if (results[1].getState() == goog.result.Result.State.ERROR) { *     alert('result2 failed'); *   } * }); * </pre> * * @param {...!goog.result.Result} var_args The results to wait on. * * @return {!goog.result.DependentResult} A new Result whose eventual value will *     be an array of values of the given Result objects. */goog.result.combineOnSuccess = function(var_args) {  var results = goog.array.clone(arguments);  var combinedResult = new goog.result.DependentResultImpl_(results);  var resolvedSuccessfully = function(res) {    return res.getState() == goog.result.Result.State.SUCCESS;  };  goog.result.wait(      goog.result.combine.apply(goog.result.combine, results),      // The combined result never ERRORs      function(res) {        var results =            /** @type {Array<!goog.result.Result>} */ (res.getValue());        if (goog.array.every(results, resolvedSuccessfully)) {          combinedResult.setValue(results);        } else {          combinedResult.setError(results);        }      });  return combinedResult;};/** * Given a DependentResult, cancels the Results it depends on (that is, the * results returned by getParentResults). This function does not recurse, * so e.g. parents of parents are not canceled; only the immediate parents of * the given Result are canceled. * * Example using @see goog.result.combine: * <pre> * var result1 = xhr.get('testdata/xhr_test_text.data'); * * // Get a second independent Result. * var result2 = xhr.getJson('testdata/xhr_test_json.data'); * * // Create a Result that resolves when both prior results resolve. * var combinedResult = goog.result.combineOnSuccess(result1, result2); * * combinedResult.wait(function() { *   if (combinedResult.isCanceled()) { *     goog.result.cancelParentResults(combinedResult); *   } * }); * * // Now, canceling combinedResult will cancel both result1 and result2. * combinedResult.cancel(); * </pre> * @param {!goog.result.DependentResult} dependentResult A Result that is *     dependent on the values of other Results (for example the Result of a *     goog.result.combine, goog.result.chain, or goog.result.transform call). * @return {boolean} True if any results were successfully canceled; otherwise *     false. * TODO(user): Implement a recursive version of this that cancels all * ancestor results. */goog.result.cancelParentResults = function(dependentResult) {  var anyCanceled = false;  var results = dependentResult.getParentResults();  for (var n = 0; n < results.length; n++) {    anyCanceled |= results[n].cancel();  }  return !!anyCanceled;};/** * A DependentResult represents a Result whose eventual value depends on the * value of one or more other Results. For example, the Result returned by * @see goog.result.chain or @see goog.result.combine is dependent on the * Results given as arguments. * * @param {!Array<!goog.result.Result>} parentResults A list of Results that *     will affect the eventual value of this Result. * @constructor * @implements {goog.result.DependentResult} * @extends {goog.result.SimpleResult} * @private */goog.result.DependentResultImpl_ = function(parentResults) {  goog.result.DependentResultImpl_.base(this, 'constructor');  /**   * A list of Results that will affect the eventual value of this Result.   * @type {!Array<!goog.result.Result>}   * @private   */  this.parentResults_ = parentResults;};goog.inherits(goog.result.DependentResultImpl_, goog.result.SimpleResult);/** * Adds a Result to the list of Results that affect this one. * @param {!goog.result.Result} parentResult A result whose value affects the *     value of this Result. */goog.result.DependentResultImpl_.prototype.addParentResult = function(    parentResult) {  this.parentResults_.push(parentResult);};/** @override */goog.result.DependentResultImpl_.prototype.getParentResults = function() {  return this.parentResults_;};
 |