Impossible !!
A Promise just resolve one value but not a stream of values.
Telling lies ๐คจ?? no papa
Out of curiosity I was exploring the possibility of a Publish-Subscribe model using Promises. The result is very interesting and it really brushed up my knowledge on call by value and call by reference too.
So here's the idea. create a function that return a tuple containing an object to subscribe
(this returns a function to unsubscribe), reset
and a resolver
function to emit values to subscribers.
function promisedPubSub() {
let callbackRegistery = Object.create(null);
const subscriptions = {
subscribe: (callback) => {
const token = `uid_${Math.random().toString(36).substring(2)}`;
callbackRegistery[token] = fn;
return () => {
delete callbackRegistery[token];
};
},
reset: () => {
callbackRegistery = Object.create(null);
}
}
return [subscriptions, () => {}]
}
// usage
const [subscriptions, resolver] = promisedPubSub();
subscriptions.subscribe((val) => { console.log('a', val); })
subscriptions.subscribe((val) => { console.log('b', val); })
resolver(1);
You can see that we can add subscribers using subscriptions.subscribe
but when we call resolver
, it is doing nothing as it is a blank function.
So let's modify the above implementation:
function promisedPubSub() {
let instance, resolver, callbackRegistery = Object.create(null);
const subscriptions = {
subscribe: (fn) => {
const token = `uid_${Math.random().toString(36).substring(2)}`;
callbackRegistery[token] = fn;
return () => {
delete callbackRegistery[token];
};
},
reset: () => {
callbacks = [];
}
}
function initiate() {
instance = null;
resolver = null;
instance = new Promise((resolve) => {
resolver = resolve;
});
instance.then((val) => {
const callbacks = Object.values(callbackRegistery);
callbacks.forEach((callback) => {
callback(val);
});
})
}
initiate();
return [subscriptions, (val) => { resolver(val); }]
}
Yea that's a mouthful. Let me explain what we're doing here.
We created an internal function initiate
that create a new Promise. As Promise constructor invokes immediately, the resolver variable got the resolve function reference.
Now when we execute the usage code, it logs a 1 b 1
means, when we emit 1 all the subscribers received that value. but if you try to emit another value nothing happen.
You already know the answer for why nothing happen.
Promise only resolve once.
So how to emit multiple values?
Answer: Recursion.
Remember we wrote an internal function initate
? we need to call the same function when the promise is resolved.
So this is how the final code looks like:
const promisedPubSub = () => {
let instance,
resolver,
callbackRegistery = Object.create(null);
const subscriptions = {
subscribe: (fn) => {
const token = `uid_${Math.random().toString(36).substring(2)}`;
callbackRegistery[token] = fn;
return () => {
delete callbackRegistery[token];
};
},
reset: () => {
instance = null;
resolver = null;
callbackRegistery = Object.create(null);
},
};
function initiate() {
instance = null;
resolver = null;
instance = new Promise((resolve) => {
resolver = resolve;
});
instance.then((val) => {
const callbacks = Object.values(callbackRegistery);
callbacks.forEach((callback) => {
callback(val);
});
setTimeout(() => {
initiate();
});
});
}
initiate();
return [
subscriptions,
(val) => {
if(resolver) {
resolver(val);
}
},
];
};
And this is the working example:
Congratulations we made the impossible a possible ๐คฉ
And that's it.
See you in next article.
Thanks,
Kiran ๐ ๐
Top comments (0)