lib/goog/promise/thenable.js

1// Copyright 2013 The Closure Library Authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS-IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15goog.provide('goog.Thenable');
16
17
18
19/**
20 * Provides a more strict interface for Thenables in terms of
21 * http://promisesaplus.com for interop with {@see goog.Promise}.
22 *
23 * @interface
24 * @extends {IThenable<TYPE>}
25 * @template TYPE
26 */
27goog.Thenable = function() {};
28
29
30/**
31 * Adds callbacks that will operate on the result of the Thenable, returning a
32 * new child Promise.
33 *
34 * If the Thenable is fulfilled, the {@code onFulfilled} callback will be
35 * invoked with the fulfillment value as argument, and the child Promise will
36 * be fulfilled with the return value of the callback. If the callback throws
37 * an exception, the child Promise will be rejected with the thrown value
38 * instead.
39 *
40 * If the Thenable is rejected, the {@code onRejected} callback will be invoked
41 * with the rejection reason as argument, and the child Promise will be rejected
42 * with the return value of the callback or thrown value.
43 *
44 * @param {?(function(this:THIS, TYPE): VALUE)=} opt_onFulfilled A
45 * function that will be invoked with the fulfillment value if the Promise
46 * is fullfilled.
47 * @param {?(function(this:THIS, *): *)=} opt_onRejected A function that will
48 * be invoked with the rejection reason if the Promise is rejected.
49 * @param {THIS=} opt_context An optional context object that will be the
50 * execution context for the callbacks. By default, functions are executed
51 * with the default this.
52 *
53 * @return {RESULT} A new Promise that will receive the result
54 * of the fulfillment or rejection callback.
55 * @template VALUE
56 * @template THIS
57 *
58 * When a Promise (or thenable) is returned from the fulfilled callback,
59 * the result is the payload of that promise, not the promise itself.
60 *
61 * @template RESULT := type('goog.Promise',
62 * cond(isUnknown(VALUE), unknown(),
63 * mapunion(VALUE, (V) =>
64 * cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),
65 * templateTypeOf(V, 0),
66 * cond(sub(V, 'Thenable'),
67 * unknown(),
68 * V)))))
69 * =:
70 *
71 */
72goog.Thenable.prototype.then = function(opt_onFulfilled, opt_onRejected,
73 opt_context) {};
74
75
76/**
77 * An expando property to indicate that an object implements
78 * {@code goog.Thenable}.
79 *
80 * {@see addImplementation}.
81 *
82 * @const
83 */
84goog.Thenable.IMPLEMENTED_BY_PROP = '$goog_Thenable';
85
86
87/**
88 * Marks a given class (constructor) as an implementation of Thenable, so
89 * that we can query that fact at runtime. The class must have already
90 * implemented the interface.
91 * Exports a 'then' method on the constructor prototype, so that the objects
92 * also implement the extern {@see goog.Thenable} interface for interop with
93 * other Promise implementations.
94 * @param {function(new:goog.Thenable,...?)} ctor The class constructor. The
95 * corresponding class must have already implemented the interface.
96 */
97goog.Thenable.addImplementation = function(ctor) {
98 goog.exportProperty(ctor.prototype, 'then', ctor.prototype.then);
99 if (COMPILED) {
100 ctor.prototype[goog.Thenable.IMPLEMENTED_BY_PROP] = true;
101 } else {
102 // Avoids dictionary access in uncompiled mode.
103 ctor.prototype.$goog_Thenable = true;
104 }
105};
106
107
108/**
109 * @param {*} object
110 * @return {boolean} Whether a given instance implements {@code goog.Thenable}.
111 * The class/superclass of the instance must call {@code addImplementation}.
112 */
113goog.Thenable.isImplementedBy = function(object) {
114 if (!object) {
115 return false;
116 }
117 try {
118 if (COMPILED) {
119 return !!object[goog.Thenable.IMPLEMENTED_BY_PROP];
120 }
121 return !!object.$goog_Thenable;
122 } catch (e) {
123 // Property access seems to be forbidden.
124 return false;
125 }
126};