| Index: mojo/public/python/mojo/bindings/promise.py
|
| diff --git a/mojo/public/python/mojo/bindings/promise.py b/mojo/public/python/mojo/bindings/promise.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c5d7d7cb641945e14af53d1b1d5e681db57d21b2
|
| --- /dev/null
|
| +++ b/mojo/public/python/mojo/bindings/promise.py
|
| @@ -0,0 +1,188 @@
|
| +# Copyright 2014 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +"""
|
| +Promise used by the python bindings.
|
| +
|
| +The API is following the ECMAScript 6 API for promises.
|
| +"""
|
| +
|
| +
|
| +class Promise(object):
|
| + """The promise object."""
|
| +
|
| + STATE_PENDING = 0
|
| + STATE_FULLFILLED = 1
|
| + STATE_REJECTED = 2
|
| + STATE_BOUND = 3
|
| +
|
| + def __init__(self, generator_function):
|
| + """
|
| + Constructor.
|
| +
|
| + Args:
|
| + generator_function: A function taking 2 arguments: resolve and reject.
|
| + When |resolve| is called, the promise is fullfilled with the given value.
|
| + When |reject| is called, the promise is rejected with the given value.
|
| + A promise can only be resolved or rejected once, all following calls will
|
| + have no effect.
|
| + """
|
| + self._onCatched = []
|
| + self._onFulfilled = []
|
| + self._onRejected = []
|
| + self._state = Promise.STATE_PENDING
|
| + self._result = None
|
| + if generator_function:
|
| + generator_function(self._Resolve, self._Reject)
|
| +
|
| + @staticmethod
|
| + def Resolve(value):
|
| + """
|
| + If value is a promise, make a promise that have the same behavior as value,
|
| + otherwise make a promise that fulfills to value.
|
| + """
|
| + if isinstance(value, Promise):
|
| + return value
|
| + return Promise(lambda x, y: x(value))
|
| +
|
| + @staticmethod
|
| + def Reject(reason):
|
| + "Make a promise that rejects to reason."""
|
| + return Promise(lambda x, y: y(reason))
|
| +
|
| + @staticmethod
|
| + def All(*iterable):
|
| + """
|
| + Make a promise that fulfills when every item in the array fulfills, and
|
| + rejects if (and when) any item rejects. Each array item is passed to
|
| + Promise.resolve, so the array can be a mixture of promise-like objects and
|
| + other objects. The fulfillment value is an array (in order) of fulfillment
|
| + values. The rejection value is the first rejection value.
|
| + """
|
| + def GeneratorFunction(resolve, reject):
|
| + state = {
|
| + 'rejected': False,
|
| + 'nb_resolved': 0,
|
| + }
|
| + promises = [Promise.Resolve(x) for x in iterable]
|
| + results = [None for x in promises]
|
| + def OnFullfilled(i):
|
| + def OnFullfilled(res):
|
| + if state['rejected']:
|
| + return
|
| + results[i] = res
|
| + state['nb_resolved'] = state['nb_resolved'] + 1
|
| + if state['nb_resolved'] == len(results):
|
| + resolve(results)
|
| + return OnFullfilled
|
| + def OnRejected(reason):
|
| + if state['rejected']:
|
| + return
|
| + state['rejected'] = True
|
| + reject(reason)
|
| +
|
| + for (i, promise) in enumerate(promises):
|
| + promise.Then(OnFullfilled(i), OnRejected)
|
| + return Promise(GeneratorFunction)
|
| +
|
| + @staticmethod
|
| + def Race(*iterable):
|
| + """
|
| + Make a Promise that fulfills as soon as any item fulfills, or rejects as
|
| + soon as any item rejects, whichever happens first.
|
| + """
|
| + def GeneratorFunction(resolve, reject):
|
| + state = {
|
| + 'ended': False
|
| + }
|
| + def OnEvent(callback):
|
| + def OnEvent(res):
|
| + if state['ended']:
|
| + return
|
| + state['ended'] = True
|
| + callback(res)
|
| + return OnEvent
|
| + for promise in [Promise.Resolve(x) for x in iterable]:
|
| + promise.Then(OnEvent(resolve), OnEvent(reject))
|
| + return Promise(GeneratorFunction)
|
| +
|
| + @property
|
| + def state(self):
|
| + if isinstance(self._result, Promise):
|
| + return self._result.state
|
| + return self._state
|
| +
|
| + def Then(self, onFullfilled=None, onRejected=None):
|
| + """
|
| + onFulfilled is called when/if this promise resolves. onRejected is called
|
| + when/if this promise rejects. Both are optional, if either/both are omitted
|
| + the next onFulfilled/onRejected in the chain is called. Both callbacks have
|
| + a single parameter, the fulfillment value or rejection reason. |Then|
|
| + returns a new promise equivalent to the value you return from
|
| + onFulfilled/onRejected after being passed through Resolve. If an
|
| + error is thrown in the callback, the returned promise rejects with that
|
| + error.
|
| + """
|
| + if isinstance(self._result, Promise):
|
| + return self._result.Then(onFullfilled, onRejected)
|
| + def GeneratorFunction(resolve, reject):
|
| + if self._state == Promise.STATE_PENDING:
|
| + self._onFulfilled.append(_Delegate(resolve, reject, onFullfilled))
|
| + self._onRejected.append(_Delegate(reject, reject, onRejected))
|
| + if self._state == self.STATE_FULLFILLED:
|
| + _Delegate(resolve, reject, onFullfilled)(self._result)
|
| + if self._state == self.STATE_REJECTED:
|
| + recover = reject
|
| + if onRejected:
|
| + recover = resolve
|
| + _Delegate(recover, reject, onRejected)(self._result)
|
| + return Promise(GeneratorFunction)
|
| +
|
| + def Catch(self, onCatched):
|
| + """Equivalent to |Then(None, onCatched)|"""
|
| + return self.Then(None, onCatched)
|
| +
|
| + def _Resolve(self, value):
|
| + if self.state != Promise.STATE_PENDING:
|
| + return
|
| + self._result = value
|
| + if isinstance(value, Promise):
|
| + self._state = Promise.STATE_BOUND
|
| + self._result.Then(_IterateAction(self._onFulfilled),
|
| + _IterateAction(self._onRejected))
|
| + return
|
| + self._state = Promise.STATE_FULLFILLED
|
| + for f in self._onFulfilled:
|
| + f(value)
|
| + self._onFulfilled = None
|
| + self._onRejected = None
|
| +
|
| + def _Reject(self, reason):
|
| + if self.state != Promise.STATE_PENDING:
|
| + return
|
| + self._result = reason
|
| + self._state = Promise.STATE_REJECTED
|
| + for f in self._onRejected:
|
| + f(reason)
|
| + self._onFulfilled = None
|
| + self._onRejected = None
|
| +
|
| +
|
| +def _IterateAction(iterable):
|
| + def _Run(x):
|
| + for f in iterable:
|
| + f(x)
|
| + return _Run
|
| +
|
| +
|
| +def _Delegate(resolve, reject, action):
|
| + def _Run(x):
|
| + try:
|
| + if action:
|
| + resolve(action(x))
|
| + else:
|
| + resolve(x)
|
| + except Exception as e:
|
| + reject(e)
|
| + return _Run
|
|
|