Promise implementation in Python Recently I made an basic port of the JavaScript Promise construct to Python. Here's the basic code:

class Promise:
    def __init__(self, fn):
        self.value = None
        self.errors = []
        self._state = "unfullfilled"

        def resolve(val): 
            if self._state == "settled":
                return
            self.value = val
            self._state = "settled"
        def reject(err):
            if self._state == "settled":
               return
            self.errors.append(err)
            self._state = "settled"
        fn(resolve, reject)

    def catch(self, fn):
        if self.errors:
            for i in self.errors:
                fn(i)
                self.errors.remove(i)
        return self

    def then(self, fn):
        if not self.errors:
            fn(self.value)
        return self

    @staticmethod
    def resolve(val):
        def g(resolve, reject):
            resolve(val)
        return Promise(g)

    @staticmethod
    def reject(val):
        def g(resolve, reject):
            reject(val)
        return Promise(g)


What could I improve in this code?
Besides that, are there any bugs in this code?
Thank you for showing your code. You really should show us how to use it, by including unit tests, a reference URL, or with comments. At a minimum the class needs a docstring.

self._state = "unfullfilled"


Typo. Consider using enum.Enum instead of str.

self.value = None


This appears to be just wrong. (Hard to tell, since no docstring describes what valid callers may do.) A fn could choose to return None, yes? The usual approach for such a sentinel is to invoke object() to create a private object, essentially an allocated memory location that other code doesn't know about. Then test equality against that. If, on the other hand, you want your API to prohibit passing None, then you'll want to add some checks for that.

self.errors.remove(i)


That's an odd way to phrase it. The usual idiom for the loop you wrote would be to .pop() the initial element. Consider switching from list to deque if lots of errors might accumulate.