// Copyright 2013 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.
goog.provide('goog.Thenable');
/**
* Provides a more strict interface for Thenables in terms of
* http://promisesaplus.com for interop with {@see goog.Promise}.
*
* @interface
* @extends {IThenable<TYPE>}
* @template TYPE
*/
goog.Thenable = function() {};
/**
* Adds callbacks that will operate on the result of the Thenable, returning a
* new child Promise.
*
* If the Thenable is fulfilled, the {@code onFulfilled} callback will be
* invoked with the fulfillment value as argument, and the child Promise will
* be fulfilled with the return value of the callback. If the callback throws
* an exception, the child Promise will be rejected with the thrown value
* instead.
*
* If the Thenable is rejected, the {@code onRejected} callback will be invoked
* with the rejection reason as argument, and the child Promise will be rejected
* with the return value of the callback or thrown value.
*
* @param {?(function(this:THIS, TYPE): VALUE)=} opt_onFulfilled A
* function that will be invoked with the fulfillment value if the Promise
* is fullfilled.
* @param {?(function(this:THIS, *): *)=} opt_onRejected A function that will
* be invoked with the rejection reason if the Promise is rejected.
* @param {THIS=} opt_context An optional context object that will be the
* execution context for the callbacks. By default, functions are executed
* with the default this.
*
* @return {RESULT} A new Promise that will receive the result
* of the fulfillment or rejection callback.
* @template VALUE
* @template THIS
*
* When a Promise (or thenable) is returned from the fulfilled callback,
* the result is the payload of that promise, not the promise itself.
*
* @template RESULT := type('goog.Promise',
* cond(isUnknown(VALUE), unknown(),
* mapunion(VALUE, (V) =>
* cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),
* templateTypeOf(V, 0),
* cond(sub(V, 'Thenable'),
* unknown(),
* V)))))
* =:
*
*/
goog.Thenable.prototype.then = function(opt_onFulfilled, opt_onRejected,
opt_context) {};
/**
* An expando property to indicate that an object implements
* {@code goog.Thenable}.
*
* {@see addImplementation}.
*
* @const
*/
goog.Thenable.IMPLEMENTED_BY_PROP = '$goog_Thenable';
/**
* Marks a given class (constructor) as an implementation of Thenable, so
* that we can query that fact at runtime. The class must have already
* implemented the interface.
* Exports a 'then' method on the constructor prototype, so that the objects
* also implement the extern {@see goog.Thenable} interface for interop with
* other Promise implementations.
* @param {function(new:goog.Thenable,...?)} ctor The class constructor. The
* corresponding class must have already implemented the interface.
*/
goog.Thenable.addImplementation = function(ctor) {
goog.exportProperty(ctor.prototype, 'then', ctor.prototype.then);
if (COMPILED) {
ctor.prototype[goog.Thenable.IMPLEMENTED_BY_PROP] = true;
} else {
// Avoids dictionary access in uncompiled mode.
ctor.prototype.$goog_Thenable = true;
}
};
/**
* @param {*} object
* @return {boolean} Whether a given instance implements {@code goog.Thenable}.
* The class/superclass of the instance must call {@code addImplementation}.
*/
goog.Thenable.isImplementedBy = function(object) {
if (!object) {
return false;
}
try {
if (COMPILED) {
return !!object[goog.Thenable.IMPLEMENTED_BY_PROP];
}
return !!object.$goog_Thenable;
} catch (e) {
// Property access seems to be forbidden.
return false;
}
};