OLD | NEW |
| (Empty) |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 """ | |
6 Promise used by the python bindings. | |
7 | |
8 The API is following the ECMAScript 6 API for promises. | |
9 """ | |
10 | |
11 import sys | |
12 | |
13 | |
14 class Promise(object): | |
15 """The promise object.""" | |
16 | |
17 STATE_PENDING = 0 | |
18 STATE_FULLFILLED = 1 | |
19 STATE_REJECTED = 2 | |
20 STATE_BOUND = 3 | |
21 | |
22 def __init__(self, generator_function): | |
23 """ | |
24 Constructor. | |
25 | |
26 Args: | |
27 generator_function: A function taking 2 arguments: resolve and reject. | |
28 When |resolve| is called, the promise is fullfilled with the given value. | |
29 When |reject| is called, the promise is rejected with the given value. | |
30 A promise can only be resolved or rejected once, all following calls will | |
31 have no effect. | |
32 """ | |
33 self._onCatched = [] | |
34 self._onFulfilled = [] | |
35 self._onRejected = [] | |
36 self._state = Promise.STATE_PENDING | |
37 self._result = None | |
38 try: | |
39 generator_function(self._Resolve, self._Reject) | |
40 except Exception as e: | |
41 # Adding traceback similarly to python 3.0 (pep-3134) | |
42 e.__traceback__ = sys.exc_info()[2] | |
43 self._Reject(e) | |
44 | |
45 @staticmethod | |
46 def Resolve(value): | |
47 """ | |
48 If value is a promise, make a promise that have the same behavior as value, | |
49 otherwise make a promise that fulfills to value. | |
50 """ | |
51 if isinstance(value, Promise): | |
52 return value | |
53 return Promise(lambda x, y: x(value)) | |
54 | |
55 @staticmethod | |
56 def Reject(reason): | |
57 "Make a promise that rejects to reason.""" | |
58 return Promise(lambda x, y: y(reason)) | |
59 | |
60 @staticmethod | |
61 def All(*iterable): | |
62 """ | |
63 Make a promise that fulfills when every item in the array fulfills, and | |
64 rejects if (and when) any item rejects. Each array item is passed to | |
65 Promise.resolve, so the array can be a mixture of promise-like objects and | |
66 other objects. The fulfillment value is an array (in order) of fulfillment | |
67 values. The rejection value is the first rejection value. | |
68 """ | |
69 def GeneratorFunction(resolve, reject): | |
70 state = { | |
71 'rejected': False, | |
72 'nb_resolved': 0, | |
73 } | |
74 promises = [Promise.Resolve(x) for x in iterable] | |
75 results = [None for x in promises] | |
76 def OnFullfilled(i): | |
77 def OnFullfilled(res): | |
78 if state['rejected']: | |
79 return | |
80 results[i] = res | |
81 state['nb_resolved'] = state['nb_resolved'] + 1 | |
82 if state['nb_resolved'] == len(results): | |
83 resolve(results) | |
84 return OnFullfilled | |
85 def OnRejected(reason): | |
86 if state['rejected']: | |
87 return | |
88 state['rejected'] = True | |
89 reject(reason) | |
90 | |
91 for (i, promise) in enumerate(promises): | |
92 promise.Then(OnFullfilled(i), OnRejected) | |
93 return Promise(GeneratorFunction) | |
94 | |
95 @staticmethod | |
96 def Race(*iterable): | |
97 """ | |
98 Make a Promise that fulfills as soon as any item fulfills, or rejects as | |
99 soon as any item rejects, whichever happens first. | |
100 """ | |
101 def GeneratorFunction(resolve, reject): | |
102 state = { | |
103 'ended': False | |
104 } | |
105 def OnEvent(callback): | |
106 def OnEvent(res): | |
107 if state['ended']: | |
108 return | |
109 state['ended'] = True | |
110 callback(res) | |
111 return OnEvent | |
112 for promise in [Promise.Resolve(x) for x in iterable]: | |
113 promise.Then(OnEvent(resolve), OnEvent(reject)) | |
114 return Promise(GeneratorFunction) | |
115 | |
116 @property | |
117 def state(self): | |
118 if isinstance(self._result, Promise): | |
119 return self._result.state | |
120 return self._state | |
121 | |
122 def Then(self, onFullfilled=None, onRejected=None): | |
123 """ | |
124 onFulfilled is called when/if this promise resolves. onRejected is called | |
125 when/if this promise rejects. Both are optional, if either/both are omitted | |
126 the next onFulfilled/onRejected in the chain is called. Both callbacks have | |
127 a single parameter, the fulfillment value or rejection reason. |Then| | |
128 returns a new promise equivalent to the value you return from | |
129 onFulfilled/onRejected after being passed through Resolve. If an | |
130 error is thrown in the callback, the returned promise rejects with that | |
131 error. | |
132 """ | |
133 if isinstance(self._result, Promise): | |
134 return self._result.Then(onFullfilled, onRejected) | |
135 def GeneratorFunction(resolve, reject): | |
136 if self._state == Promise.STATE_PENDING: | |
137 self._onFulfilled.append(_Delegate(resolve, reject, onFullfilled)) | |
138 self._onRejected.append(_Delegate(reject, reject, onRejected)) | |
139 if self._state == self.STATE_FULLFILLED: | |
140 _Delegate(resolve, reject, onFullfilled)(self._result) | |
141 if self._state == self.STATE_REJECTED: | |
142 recover = reject | |
143 if onRejected: | |
144 recover = resolve | |
145 _Delegate(recover, reject, onRejected)(self._result) | |
146 return Promise(GeneratorFunction) | |
147 | |
148 def Catch(self, onCatched): | |
149 """Equivalent to |Then(None, onCatched)|""" | |
150 return self.Then(None, onCatched) | |
151 | |
152 def _Resolve(self, value): | |
153 if self.state != Promise.STATE_PENDING: | |
154 return | |
155 self._result = value | |
156 if isinstance(value, Promise): | |
157 self._state = Promise.STATE_BOUND | |
158 self._result.Then(_IterateAction(self._onFulfilled), | |
159 _IterateAction(self._onRejected)) | |
160 return | |
161 self._state = Promise.STATE_FULLFILLED | |
162 for f in self._onFulfilled: | |
163 f(value) | |
164 self._onFulfilled = None | |
165 self._onRejected = None | |
166 | |
167 def _Reject(self, reason): | |
168 if self.state != Promise.STATE_PENDING: | |
169 return | |
170 self._result = reason | |
171 self._state = Promise.STATE_REJECTED | |
172 for f in self._onRejected: | |
173 f(reason) | |
174 self._onFulfilled = None | |
175 self._onRejected = None | |
176 | |
177 | |
178 def _IterateAction(iterable): | |
179 def _Run(x): | |
180 for f in iterable: | |
181 f(x) | |
182 return _Run | |
183 | |
184 | |
185 def _Delegate(resolve, reject, action): | |
186 def _Run(x): | |
187 try: | |
188 if action: | |
189 resolve(action(x)) | |
190 else: | |
191 resolve(x) | |
192 except Exception as e: | |
193 # Adding traceback similarly to python 3.0 (pep-3134) | |
194 e.__traceback__ = sys.exc_info()[2] | |
195 reject(e) | |
196 return _Run | |
OLD | NEW |