Python Forum
Thread Rating:
  • 2 Vote(s) - 2.5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Promise
#1
Earlier today, I had the opportunity (lol) to write a javascript fallback function for Promises for browsers which don't support Promises.  Which... is pretty much only IE (edge supports them just fine).  For reference: https://developer.mozilla.org/en-US/docs...ts/Promise

The basic idea, is that you create a promise and immediately return it, so the caller can attach callbacks which will be called when the Promise is fulfilled (...when the callee fullfills it's Promise that it'll eventually finish).  The use case for javascript is to make code look less disgusting when working with ajax calls.

For example, let's say you had a function get(url), which took, as a string argument, the url to request, and returned a Promise.  You could then write the following:
get("/some_content.json")
    .then(function(data) {
        alert(data);
    });

console.log("this will happen before the ajax callback");
It's basically just syntactic sugar, and is mostly useless now that javascript supports async/await, but that's beside the point.  The point is that it's actually pretty neat, and one of the cool parts is that you can chain data from one promise to the next, to create semi-functional programs.  And it got me to wondering how hard it would be to writing the same functionality in Python.

And it turns out... not very difficult at all.

class STATES:
    Pending = 0
    Resolved = 1
    Rejected = 2


class Promise:
    def __init__(self, worker):
        keys = [STATES.Resolved, STATES.Rejected]

        self.state = STATES.Pending
        self.callbacks = {key: [ ] for key in keys}
        self.content = {key: None for key in keys}

        def callback_generator(state):
            def callback(response=None):
                self.content[state] = response
                self.state = state
                self.__run(state)
            return callback

        worker(
            callback_generator(STATES.Resolved),
            callback_generator(STATES.Rejected)
        )

    def __run(self, key):
        # a callback is only ever called a single time
        while self.callbacks[key]:
            callback = self.callbacks[key].pop(0)
            # chain the output of one callback into the input of the next
            response = callback(self.content[key])
            if response is not None:
                self.content[key] = response

    def __chain_method(self, state, callback):
        self.callbacks[state].append(callback)

        # register the new promise's callbacks with the parent, so data/errors
        # will chain
        def worker(resolve, fail):
            self.callbacks[STATES.Resolved].append(resolve)
            self.callbacks[STATES.Rejected].append(fail)

        future = Promise(worker)
        future.content = self.content
        future.state = self.state
        if self.state == state:
            self.__run(state)

        return future

    def then(self, callback):
        return self.__chain_method(STATES.Resolved, callback)

    def catch(self, callback):
        return self.__chain_method(STATES.Rejected, callback)


if __name__ == "__main__":
    def callback(resolve, fail):
        print("before resolve")
        resolve(5)
        print("after resolve")
        # fail(Exception("boop"))

    future = Promise(callback)
    future.then(lambda x: x**2).then(print)
    #.catch(lambda err: print(f"Uh oh: {err}"))
Reply


Messages In This Thread
Promise - by nilamo - Aug-16-2017, 04:09 AM
RE: Promise - by nilamo - Aug-17-2017, 08:44 PM
RE: Promise - by nilamo - Aug-18-2017, 09:36 PM

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020