JavaScript’s Deferred Promise Pattern
ฝัง
- เผยแพร่เมื่อ 25 ส.ค. 2024
- The deferred pattern is a great way to add new flexibility to the way you use promises.
Looking for more TypeScript goodness? Check out my blog post on better utility functions with TS Generics: shaky.sh/ts-ge...
My Links
shaky.sh
shaky.sh/tools
If you’ve used the deferred pattern before, share your use case in a comment!
my initial thought was "what that's stupid" but through the example I thought "yeah that can be useful". this was because the way it originally seemed was that you returned the deferred object itself, and the caller of the function got back the promise as well as a way to resolve and a way to reject, but since you just return the promise it's just a more compact way to do it than you would with a promise
I'm most a backend dev, but this is super handy even for certain asynchronous operations *I* would do. Love it!
it'd be super cool to see more about what you're up to with the audio APIs!
Currently implementing a side project using OpenAI and Audio APIs, Siri like😂😂
Right now, just playing around to learn the APIs. Wanna learn more about how to process audio. But if I come up with any small projects, I’ll be sure to share them!
this is great, I used to to create a chainable promise returning Api call!
Thank you so much for helping beginners advance their level of JS knowledge. Your top-level tutorials are always presented so clearly and concisely that even I can benefit.
Super useful pattern for mocked promises in tests. Gives test full control of promise resolution or rejection
Hi @Andrew. This was a great topic and you did teach us well, but if you give us more use case for this, it would be better.
Wow thank you, I wish you post more advanced content like this.
Would love seeming more patterns videos like this!
It's unusual for me to a actually use the code from a video including the most interesting and I have been learning a lot from many but it is the second time I am using the code from your video to play with it and such
I think that pattern makes your code way less readable when you could just say
const asyncBlob = new Promise(resolve => ...)
and wrap it around the media recorder stop event listener and then return asyncBlob in the returned function. I think using a plain promise here creates a more distinct point of definition for how the value of the blob gets resolved and what logic is required to resolve it.
This just feels like abstraction for the purpose of abstraction.
Bro you missed the stop operation from the caller.
const endRecording = recordStreamAsBlob(…)
….
const blob = await endRecording()
@@Tony-bu1rj No I didn't 🤨 Here's the full implementation without any need for a promise wrapper
export function recordStreamAsBlob(stream: MediaStream) {
const mediaRecorder = new MediaRecorder(stream);
const chunks: Array = [];
mediaRecorder.addEventListener("dataavailable", (e) => {
chunks.push(e.data);
})
const asyncBlob = new Promise(resolve => {
mediaRecorder.addEventListener("stop", () => {
const blob = new Blob(chunks, { type: "audio/ogg; codecs=opus" });
resolve(blob);
})
})
mediaRecorder.start();
return () => {
mediaRecorder.stop();
return asyncBlob;
}
}
// now that can be called the same way
const endRecording = recordStreamAsBlob(stream);
const blob = await endRecording();
Way to go, Son!
Thanks mom
This is really cool. Please more patterns!!
Great video. I've never seen this pattern before. Thank you for sharing!
This is super cool. Will be looking for ways to use it going forward!
I really like this patterns!
Great video🎉
I must say I never really cared about TypeScript that much, but the way you explain TypeScript and the possibilities / ease of use is making me interested.
Huge compliment, thanks! I’m curious, what has made you dismiss TS in the past?
@@andrew-burgess TypeScript has always seemed like a thing that was great, but not necessary if you're just making hobby projects. I've recently started making websites "professionally", and currently my only reason why I haven't used it before is lack of time due to school and my boss doesn't want me to spend time on TypeScript. Right now at least, since we have a lot of open projects at the moment. But maybe when it's slow at work I will check it out, and once I have a bit more freetime :)
@@darealmexury the funny thing is in the long run typescript is going to gain you time by avoiding stupid error that are completely missable with plain JS
I remember I first saw the blob API, I was so confused
There is a simple enough way to do this without this pattern, just wrap setting up the event handlers inside the promise, bind it to a variable, then start, and return your function returning the promise. Not to say this pattern isn't useful, but in my experience it's very occasionally useful for very convoluted async control flows.
Yeah deferred rocks, it essentially encapsulates in a reusable way whenever I've done new Promise(.. and captures the resolve function by binding it to an outer variable. Deferred is way less to reason about.
great stuff!
Love this pattern! I’m using something very similar with the spawn child API to capture the exit event. I need to know the return code, and that’s only available on exit callback.
That’s a great use case!
Wow so cool!
I don't get it, where did you get that "Deferred" type from?
The code itself, I just wrote; it’s, like, 8 lines. The pattern has been around for a long time, not something I came up with. I know it was popular with jQuery back in the day, but it’s still a useful pattern in many cases. I’m gonna see if I can come up with a list of ways to use it
Somehow I totally missed the first part of the video (-‸ლ)
I find the deferred promises a very very dirty way to achieve things. It feels so bad to me to expose the internals of promise (resolve and reject). I am the idea (and I actually been able to make the same thing with) that if ones can master thenables, it will be able to achieve a more readable / cleaner code that do not expose Promises internals. Also, await supports thenables as well, so a 'then' method gets called when used with await.
Interesting take, thanks for sharing! I’m not familiar with thenables, but I’m gonna check them out.
Okay, I looked into thenables, looks like we’re mainly talking about normal promises 👍. Curious to see how you would refactor this example to use a regular promise and not Deferred.
But also, within the context of the `recordStreamAsBlob` function example, the logic inside it will always have to directly access resolve and reject, even if somehow you can put it all inside a Promise callback. So I don’t really agree that you aren’t exposing the Promise internals. Inside, you’ll have access to the promise as well as its resolve and reject functions, and outside you’ll just have access to the promise you return.
Unless I’m totally missing what you’re saying here 🙏
@@andrew-burgess I also wrote this comment like three times because I was trying to link the codesandbox I created as an example (starting from the code you shared), but probably TH-cam bots were keeping deleting it ahah.
Yes, we're kinda talking about normal promises. The difference here is that you are able to determine whether your code is synchronous or asynchronous or will run on microtask queue. So you can have a way more dynamic way to handle your operations. The cost is that thenables are completely "raw", so you have to implement your queue system for callbacks, determine what a 'then' should be able to return and so on.
About the "exposing internals", my point of view is that if you access to something that is passed to you from an internal / native implementation (just like resolve and reject) and you are bringing it outside (outer scope), you are exposing it. I feel some code smell in that case (so, by using the deferred pattern).
On the other side, if you move everything inside a Promise, it feels to be better because you are not exposing anything and you are doing everything in that scope. Of couse this is trade-off with indentation (one indentation level more than with deferred). Also, its worth noticing that you are adopting another different pattern here, which involves (let's call like this) an "unsubscription function" as return value instead of the promise. Otherwise, using just the promise you wouldn't be able to stop the recording, and this is not okay.
That's why I think that thenables here are the best way.
I'll share the url of the code I refactored with thenables in the next comment. Maybe in a way that it is not a URL to bots.
On the same project I've also added an example with the function instead of the class. Let me know!
@@AlexanderCerutti I think youtube pulled your comment with the link again, but I was able to get the URL from a notification, so taking a look now! Thanks for all this detail!
Sorry, but RxJS is love and life. Would lean towards them every day of the week.
Yeah, observables/async iterations are 🔥
or you could wrap the event listeners in a promise and resolve that directly
return () => {
return new Promise((resolve, reject) => {
mediaRecorder.addEventListener("stop", () => {
const blob = ...
resolve(blob);
});
mediaRecorder.stop();
});
}
Usage:
const stopRecording = recordStreamAsBlob(stream);
const blob = await stopRecording();
Would blob be undefined in the example without Deferred because event handlers are called asynchronously or something like that being put in the event loop queue and stuff? So blob will never be assigned before the function returns? Is it because of that or am I missing something?
Yep, you got it!
Cool
Hey, Andrew do you ever pause to consider that half your audience gives up midway on your videos because you're running thru your content too quickly? TH-cam isn't running out of space anytime soon. An extra 3+ minutes isn't going to hurt. Not trying to be jackass.
Good feedback, thanks my guy. 💪
I disagree! Thought this was the perfect length, any longer I probably wouldn't have watched.
@@andrew-burgess sure thing, my guy 💪💪
It's fine, if you want it slower there's an option for that.
The length is fine but I think the point of using this pattern was missing and/or wasn't well explained.