Wait wait wait. It makes more sense for the Promise itself to handle threading, since almost anything that uses it would involve threading anyway.
import threading 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 self._lock = threading.Lock() self._thread = threading.Thread(target=worker, args=( callback_generator(STATES.Resolved), callback_generator(STATES.Rejected))) self._thread.start() def join(self): with self._lock: self._thread.join() def __run(self, key): # a callback is only ever called a single time with self._lock: 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): with self._lock: 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) import time def delayed(delay): def runner(resolved, failed): time.sleep(delay) resolved() return Promise(runner) if __name__ == "__main__": def callback(*args): print("Inside callback") print("Before delayed callback") future = delayed(2.5).then(callback) print("After delayed callback") for _ in range(5): print("waiting...") time.sleep(1) future.join()
Output:Before delayed callback
After delayed callback
waiting...
waiting...
waiting...
Inside callback
waiting...
waiting...