Last active
January 10, 2017 00:00
-
-
Save vbuaraujo/a51eea7bcbcb23bfb9667888758b73cb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Promise(object): | |
def __init__(self, worker=None): | |
# A promise may be 'pending' or fulfilled. If it's fulfilled, then it | |
# may be 'resolved' or 'rejected'. | |
self.status = 'pending' | |
# After it's fulfilled, it has a value. | |
self.value = None | |
# Before it's fulfilled, computations waiting on the value of the | |
# promise are queued for execution when it gets fulfilled (either | |
# resolved or rejected). | |
self.callback_queue = {'resolved': [], 'rejected': []} | |
# Call the worker function passing methods which, when called, | |
# resolve or reject the created promise. | |
if worker: | |
worker(self._resolve, self._reject) | |
def _fulfill(self, status, val): | |
"""Fulfill the promise with either 'resolved' or 'rejected' `status`, | |
and value `val`. Run all the queued callbacks for the given status. | |
""" | |
if self.status != 'pending': | |
print "Oops, promise already fulfilled. Doing nothing!" | |
return | |
self.status = status | |
self.value = val | |
for callback in self.callback_queue[status]: | |
callback(val) | |
self.callback_queue = None # this shall not be used again | |
def _resolve(self, val): | |
self._fulfill('resolved', val) | |
def _reject(self, val): | |
self._fulfill('rejected', val) | |
def queue_callback(self, status, callback): | |
"""Enqueue callback to be run when the promise gets fulfilled with status | |
`status`. If the promise is already fulfilled with that status, run the | |
callback right away. | |
""" | |
if self.status == 'pending': | |
self.callback_queue[status].append(callback) | |
elif self.status == status: | |
callback(self.value) | |
else: | |
pass | |
# Now comes the tricky part. | |
def then(self, callback): | |
"""`then` does the following: | |
- It creates and returns a new promise. (Now we have two problems, | |
I mean, promises: the `self` promise, and the newly created one.) | |
- The `callback` function will only be called when and if the `self` | |
promise gets resolved (fulfilled with 'resolved' status). | |
- When `callback` is eventually called, whatever value it returns, the | |
new promise *resolves* with that value. | |
""" | |
new_promise = Promise() | |
def chained_resolve_callback(val): | |
# Run callback with the result of the `self` promise once it | |
# fulfills, and then resolve the `new_promise` with the result of | |
# the callback. | |
newval = callback(val) | |
new_promise._resolve(newval) | |
self.queue_callback('resolved', chained_resolve_callback) | |
return new_promise | |
### Example. | |
# Ideally we would have something asynchronous resolve the promise (as is the | |
# case when we use $.ajax), but here we will simply make a promise worker which | |
# drops its own 'resolve' method in a global variable so we can resolve the | |
# promise later. | |
promise_resolver = None | |
def example_worker(resolve, reject): | |
global promise_resolver | |
promise_resolver = resolve | |
p1 = Promise(example_worker) | |
# Let's make a callback to be called when p1 resolves, which takes whatever | |
# value it got and squares it. Then let's hang it on `p1` with `then`. The | |
# result is a second promise whose callback will run when `p1` resolves, and | |
# will itself resolve with the square of what `p1` resolved with. | |
def squarer_callback(val): | |
return val * val | |
p2 = p1.then(squarer_callback) | |
# Now let's hang *another* callback on `p2`, which will run whenever `p2` | |
# resolves and print the value it got resolved with. | |
def printer_callback(val): | |
print val | |
p3 = p2.then(printer_callback) | |
# Finally, we call p1's resolver, and watch the magic happen. | |
# p1 will resolve with 3, p2 will resolve with 9, and p3 will print it. | |
promise_resolver(3) | |
### Other details about JavaScript promises not implemented here: | |
# | |
# If a promise worker/callback raises an exception, the promise *rejects* with | |
# the exception value. | |
# | |
# If a promise p1 resolves with *another* promise (e.g., p1's worker/callback | |
# calls resolve(p2), where p2 is another promise), then p1 fulfills the same way | |
# as p2, i.e., if p2 resolves, then p1 resolves, and if p2 rejects, then p1 | |
# rejects, with the same value. `p1` basically becomes a repeater of whatever p2 | |
# fulfills with. | |
# | |
# Also, I forgot to implement `catch`. | |
# | |
### Other resources: | |
# | |
# I first learned about promises here: | |
# | |
# https://qntm.org/files/promise/promise.html | |
# |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment