Promise Pool - Leetcode 2636 - JavaScript 30-Day Challenge
Skills:
Systems Design Basics80%
Key Takeaways
The video demonstrates how to implement a promise pool in JavaScript, solving the Leetcode 2636 challenge, utilizing tools like Promise.all and async/await for efficient asynchronous execution.
Full Transcript
hey everyone welcome back and let's write some more neat code today so today let's solve the problem promise pool took a while for them to release this problem today I don't know if somebody at leak code was sleeping but this is a really good problem it's pretty challenging but it's also really fun in my opinion we're given an array of asynchronous functions and a pool limit n and we want to return an asynchronous function called Promise pool and even though it takes a list of asynchronous functions it should respond with a single promise that resolves when all of the input functions have resolved so at first this kind of sounds like promise dot all if you're familiar with that from JavaScript but there's a subtle difference well not super subtle they explicitly tell us that there's a limit to the number of concurrent promises that can be active at any point in time that can be pending at any point in time so really this is actually a lot closer to what a thread pool is if you've heard of that the similarities are that we do have some type of of concurrency limit between thread pools and promise pools also there is sort of a task queue we're going to have like this list of functions let's say there's 10 functions but our limit n is actually equal to two so we can only execute two Promises at a time and to understand this even better let's just take a quick look at the example it's a pretty good one first of all let's look on the inside because I think that's a little bit easier we have a promise being created that will resolve after some period of time where does this time come from well this promise is being returned from this Anonymous method that is being assigned to this sleep variable it takes a parameter time so basically this method when it calls will resolve after this many milliseconds straightforward and we're passing in two methods into this promise pool of course they're both asynchronous but see the pool limit here is one that means only one of these sleeps can be active at any point in time I guess it doesn't matter what order we execute the men because either way we're gonna have to wait 400 milliseconds and then have to wait 500 milliseconds or in the opposite order the total is going to be 900 milliseconds when both of the promises will have resolved so that's when this console log will execute so it kind of sounds simple but coding it up is actually kind of tricky there's many common pitfalls and I'll try to explain those to you so first of all this is a synchronous function so what should we be returning from here and it's kind of obvious from the example promise pool returns a promise because we're calling the then callback on it so here we absolutely should return a promise that we're going to construct and we know that in promises we pass in a method usually we pass in resolve and reject I'll just tell you now in this promise we don't really need reject they tell us that in the problem description and we're going to call resolve only after all of the promises have resolved so I'm just going to write that down here some where we know we're gonna have to move this maybe somewhere we definitely don't want to call it immediately but we know we have to call it at some point so what should we start doing immediately well we should probably start going through this array of functions so I'm actually going to create a pointer I which is going to let us do that because we know that we might not be able to go through all of them immediately but initially we can probably do at least n of them like this and then on each function we want to probably initiate it we'll call it so at the I Index this is a function and then we want to call it I don't think we actually have to pass in any parameters it doesn't look like we were given any arguments so let's keep it simple for now but this is pretty simple this will start like n promises but it could be possible that we have a bunch more that we need to do so how do we kind of do that well normally when one of these promises completes we want to start another one how can we do that logically well the simplest way is typically with then and what do we want to do when one of these completes well we want to execute some piece of code which probably just using the eye pointer because it is accessible from here we can just maybe do the exact same thing we did here so it's almost recursive in nature right we're calling another function like this now one important thing we probably shouldn't forget to increment our I pointer we can do that down here like I plus plus but we can also do it over here because this will be incremented after we execute this so when one of these completes we start another one but how do we know we've reached the limit like in here maybe we get to the point where I is equal to functions dot length we've reached the end of the functions array we don't want an out of bounds error down here so this is sort of our base case and this is probably where we want to call our resolve method so we can get rid of it down here but believe it or not this has a bug as well just because our eye pointer reaches out of bounds that means we have no more promises or functions left to call but that doesn't mean all of the pending promises have completed so how do we even keep track of that it's not super straightforward but in our case we kind of do need a separate variable for that so I'm going to create in progress initially it's going to be zero as well and every time we start one of these promises we then want to say in progress plus plus and when one of these completes which we know from the Callback here then we can in here say progress minus minus so now to update our base case we want to execute the base case when this is true but also when in progress is equal to zero we have three equals because JavaScript is dumb there I said it okay and resolve sort of acts like a return so this is kind of like our return like if this executes we probably don't need to worry about about this but you can see that this is kind of messy so I'm actually going to go ahead and move this into a separate function that I'm going to Define down here which I'm just going to call callback I guess and then copy and paste it and get rid of this so that's no longer an anonymous function but it is definitely more readable so now we're going to use this callback down over here now believe it or not there is another bug here and that is what happens if functions array is empty well this Loop isn't going to execute or actually it is going to execute assuming that n is positive and we're going to get an out of bounds error here so here before we execute the loop let's also check that I is less than functions.length and now uh back to my earlier point if functions is empty yes now this Loop will not execute which is good we don't get an out of bounds error here but this won't execute I mean this is just a function definition so nothing in here is really going to execute you if the functions passed in are empty which means resolve is also never going to execute so this is going to be pending forever so what we can do up here is add a base case sort of which is that if I is equal to uh functions dot length or you could just say not functions probably we each can just call resolve up here and there are still bugs here when we normally execute a function we do want to increment the pointer for the next time and we see we are doing that up here but we're not doing it down here so what we could do is increment the pointer down here but at this point you kind of see that we have some duplicate code here we're calling a function here and we're also calling a function here and every time we call a function we want when it's completed to then execute the Callback so actually we're missing this piece of code here too which at this point should kind of encourage you to clean up this code to make it more reusable and less error prone and I see that we're also missing this plug plus plus so let me add that as well that'll help us kind of refactor the similarities and differences and another bug believe it or not is here if we have reached out of bounds of the functions which means we have no more left to call but we still have some in progress so resolve is not going to execute but the rest of this is going to execute which means our eye pointer is going to be out of bounds so that's a problem so I'm going to rewrite this to just check if I is less than functions.length then we want to execute this function and increment I at the same time but else if in progress is equal to zero and I is implicitly equal to the length because that's else from this condition and getting rid of this part then in this case we can go ahead and resolve we have no more pending in progress function so this is definitely tricky and we can probably move this up to the function call as well so at this point I'm thinking of just moving all this code inside of the single callback method because this problem is pretty much recursive in nature you can see the functions being called from within itself so I'm just going to cut all of this and then move it down here and the variables are defined outside because we want them to be persistent every function call should be able to share these variables that's why we're defining them up there now to combine these two pieces of code we can either execute a function with a loop or we can execute it with an if statement I prefer the loop because that of course will let us execute the first n functions and then on subsequent times that this callback is called we'll find maybe that one of the promises just completed therefore the in progress value was decremented and then we should be able to do another function call but how do we keep track of that because this is going to try to execute n calls every single time well that's kind of why we have our in progress value in the first place in progress is the one that should always be less than n there's going to be cases probably where I exceeds n so that's why I is bounded by the length of the function and in progress where should this be called should we just move that to the beginning of the Callback But ultimately we want to call this callback yes it's a recursive function but we want to make sure we call it and I don't think the return value is actually needed here because we are inside of a promise but back to this in progress when do we want to decrement it well only when A Promise complete so we should probably just move this inside of this callback over here and defining it differently like this an anonymous function which is now actually going to do two things it's going to decrement the in progress and it's going to call the Callback recursively after every promise completes so now we're starting to get pretty close to the solution here here we don't really need this piece of code because the loop will make sure that like the Callback is executed until this condition has no longer been met so let's get rid of this part and lastly this is sort of like our base case isn't it if in progress is equal to zero well that's not enough clearly right we need I equal to functions dot length and this is the case then we resolve and you can see we kind of have a duplicate up there so we probably don't need that anymore so I'm just going to take this bottom part and move it up there and we have finally gotten pretty close to the solution we wanted you can see there's not a ton of code left we are handling the base case where immediately executing n functions if we can and then we always restart one as soon as one completes so let's go ahead and run this to make sure that it works and as you can see yes it does and it's pretty efficient now for the second solution let's go back to to the idea of Promise all but the problem is remember we can't necessarily execute all of these promises at the same time that's what promise all does simultaneously executes all of them but we can only execute up to n of them so instead of passing them in in line first let's actually get a subset of those functions so what we could do is take the first n functions and then pass them into promise.all and then await This Promise so in this case we're going to be using the async away pattern but even if we do that we don't want to just execute the first n promises after each of these is complete we want to execute the next one in the list how do we keep track of that well again we can just use a pointer in this case I'm going to use I but I'm also here going to define a function which is going to be the Callback which after one of the functions completes we want to execute this piece of code so after a function complete leads what do we want to do we want to then get functions at index I call it and in this case let's await it and if we're going to do that we probably also have to make this a sync and also let's remember to increment that I pointer now after one of these is complete what do we want to do we probably just want to call the Callback again remember and before we were doing it with the then pattern but in this case we're using a sink away we're awaiting this line so that we know for sure that that is complete before we then execute the next callback which will then execute another function but this looks like it's going to go on forever we forgot to add our base case notice this is a recursive function the base case in this case is going to be functions.length or rather I is going to be equal to functions.length and if that's the case then we simply return there is no need to resolve here because we're not actually creating a promise here this function is returning a promise which in this case looks like it's just going to return undefined anyway okay so how can this be used with promise all well it's actually simpler than you might think why not just pass n instances of this function into promise.all each of them will execute in parallel and then when each of them is complete it looks like it's doing exactly what we want it will then start another one of them so that at any point in time only n of them will be executing because we're going to pass in N promises and to do that let's actually go ahead and build those promises so I'm going to say const n promises is going to be equal to we could create an empty array and just say while I is less than n we're going to go ahead and say n promises push so then when we push to this array of promises we don't push one of the functions remember because when one of these promises completes we then want a another one to be triggered so we have to add some extra Behavior so we kind of added this callback wrapper function around this function so that when this is done then we execute another function so then you might think let's just pass in the Callback well remember the Callback is the actual function itself but we want this to be an array of promises so we actually have to call the Callback so this will create a promise maybe it evaluates immediately or maybe it's still pending but then we take this array of promises and then pass it to promise.all which will then wait for all of those promises to complete so the last question you might have is why does this await not just wait for n promises why does this necessarily wait for every single function to be called and that is because if I have promise one and promise two and these are the only two promises I pass into this guy but it actually turned out we had three function calls what's actually going to happen is promise one is not going to complete believe it or not until the other promise that it triggers also complete so this is going to create another promise and you know that because this is the recursion we're waiting for the function call to be completed but this wrapper promise this is a promise around another promise and it's going to keep going like that because that's what recursion is this might keep calling other promises and then when Like This Promise completes we pop back up here when this promise completes we pop back up and then when the root promise completes and the other root promise completes that's when promise.all says okay we're done and then we can stop and that's when this promise will no longer be pending and actually I just noticed one last bug here which is that we're taking this eye pointer but we're never actually incrementing it and we actually don't want I to be incremented in this case because we're incrementing it already inside of the function and we don't want to corrupt that value so let's actually take this and rewrite it a pretty JavaScript way now that you kind of know exactly what the solution is we have here an empty array but if we want to initialize an array of n values we can do that like this well I guess it doesn't actually have n values but we've allocated the memory for it and then we can fill it with a bunch of undefined values or you can just pass in like one you could pass in anything you want here really I'll pass in one and then for each one value we can map it to something else but actually in this case I'm thinking maybe we can't fill it with one because this function is going to be passed in undefined there's no parameter here but normally what map does is it takes in some function usually an inline function and it takes every value from the array and Maps it to something else in our case we want the result of this callback we actually want that to be called so we're just going to pass in this function we don't have to call it we just pass in the function and map will call that for us so this is pretty much doing what the previous code was but we're not corrupting our I index so now let's run this code to make sure that it works and as you can see yes it does but if you found this helpful please like And subscribe if you're preparing for coding interviews check out neat code.io it does a ton of free resources to help you prepare thanks for watching and I'll see you soon
Original Description
Solving Day 13 of the Leetcode 30-day javascript challenge. Today implement a promise pool which is pretty challenging.
🚀 https://neetcode.io/ - A better way to prepare for Coding Interviews
🥷 Discord: https://discord.gg/ddjKRXPqtk
🐦 Twitter: https://twitter.com/neetcode1
🐮 Support the channel: https://www.patreon.com/NEETcode
⭐ BLIND-75 PLAYLIST: https://www.youtube.com/watch?v=KLlXCFG5TnA&list=PLot-Xpze53ldVwtstag2TL4HQhAnC8ATf
💡 DYNAMIC PROGRAMMING PLAYLIST: https://www.youtube.com/watch?v=73r3KWiEvyk&list=PLot-Xpze53lcvx_tjrr_m2lgD2NsRHlNO&index=1
Problem Link: https://leetcode.com/problems/promise-pool/description/
0:00 - Read the problem
1:14 - Read example
2:15 - Solution One
11:55 - Solution Two
leetcode 2636
#neetcode #leetcode #python
Watch on YouTube ↗
(saves to browser)
Sign in to unlock AI tutor explanation · ⚡30
Playlist
Uploads from NeetCodeIO · NeetCodeIO · 0 of 60
← Previous
Next →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
Leetcode 149 - Maximum Points on a Line - Python
NeetCodeIO
Design Linked List - Leetcode 707 - Python
NeetCodeIO
Minimum Time to Collect All Apples in a Tree - Leetcode 1443 - Python
NeetCodeIO
Design Browser History - Leetcode 1472 - Python
NeetCodeIO
Number of Good Paths - Leetcode 2421 - Python
NeetCodeIO
Flip String to Monotone Increasing - Leetcode 926 - Python
NeetCodeIO
Maximum Sum Circular Subarray - Leetcode 918 - Python
NeetCodeIO
Find Closest Node to Given Two Nodes - Leetcode 2359 - Python
NeetCodeIO
Concatenated Words - Leetcode 472 - Python
NeetCodeIO
Data Stream as Disjoint Intervals - Leetcode 352 - Python
NeetCodeIO
LFU Cache - Leetcode 460 - Python
NeetCodeIO
N-th Tribonacci Number - Leetcode 1137
NeetCodeIO
Best Team with no Conflicts - Leetcode 1626 - Python
NeetCodeIO
Greatest Common Divisor of Strings - Leetcode 1071 - Python
NeetCodeIO
Shortest Path in a Binary Matrix - Leetcode 1091 - Python
NeetCodeIO
Insert into a Binary Search Tree - Leetcode 701 - Python
NeetCodeIO
Delete Node in a BST - Leetcode 450 - Python
NeetCodeIO
Shuffle the Array (Constant Space) - Leetcode 1470 - Python
NeetCodeIO
Fruits into Basket - Leetcode 904 - Python
NeetCodeIO
Number of Subarrays of size K and Average Greater than or Equal to Threshold - Leetcode 1343 Python
NeetCodeIO
Naming a Company - Leetcode 2306 - Python
NeetCodeIO
As Far from Land as Possible - Leetcode 1162 - Python
NeetCodeIO
Shortest Path with Alternating Colors - Leetcode 1129 - Python
NeetCodeIO
Minimum Fuel Cost to Report to the Capital - Leetcode 2477 - Python
NeetCodeIO
Count Odd Numbers in an Interval Range - Leetcode 1523 - Python
NeetCodeIO
Contains Duplicate II - Leetcode 219 - Python
NeetCodeIO
Path with Maximum Probability - Leetcode 1514 - Python
NeetCodeIO
Add to Array-Form of Integer - Leetcode 989 - Python
NeetCodeIO
Unique Paths II - Leetcode 63 - Python
NeetCodeIO
Minimum Distance between BST Nodes - Leetcode 783 - Python
NeetCodeIO
Design Hashmap - Leetcode 706 - Python
NeetCodeIO
Range Sum Query Immutable - Leetcode 303 - Python
NeetCodeIO
Binary Tree Zigzag Level Order Traversal - Leetcode 103 - Python
NeetCodeIO
Middle of the Linked List - Leetcode 876 - Python
NeetCodeIO
Course Schedule IV - Leetcode 1462 - Python
NeetCodeIO
Single Element in a Sorted Array - Leetcode 540 - Python
NeetCodeIO
Capacity to Ship Packages - Leetcode 1011 - Python
NeetCodeIO
IPO - Leetcode 502 - Python
NeetCodeIO
Minimize Deviation in Array - Leetcode 1675 - Python
NeetCodeIO
Longest Turbulent Array - Leetcode 978 - Python
NeetCodeIO
Last Stone Weight II - Leetcode 1049 - Python
NeetCodeIO
Construct Quad Tree - Leetcode 427 - Python
NeetCodeIO
Find Duplicate Subtrees - Leetcode 652 - Python
NeetCodeIO
Sort an Array - Leetcode 912 - Python
NeetCodeIO
Ones and Zeroes - Leetcode 474 - Python
NeetCodeIO
Remove Duplicates from Sorted Array II - Leetcode 80 - Python
NeetCodeIO
Maximum Twin Sum of a Linked List - Leetcode 2130 - Python
NeetCodeIO
Concatenation of Array - Leetcode 1929 - Python
NeetCodeIO
Symmetric Tree - Leetcode 101 - Python
NeetCodeIO
Check Completeness of a Binary Tree - Leetcode 958 - Python
NeetCodeIO
Construct Binary Tree from Inorder and Postorder Traversal - Leetcode 106 - Python
NeetCodeIO
Find Peak Element - Leetcode 162 - Python
NeetCodeIO
Accounts Merge - Leetcode 721 - Python
NeetCodeIO
Binary Tree Preorder Traversal (Iterative) - Leetcode 144 - Python
NeetCodeIO
Binary Tree Postorder Traversal (Iterative) - Leetcode 145 - Python
NeetCodeIO
Number of Zero-Filled Subarrays - Leetcode 2348 - Python
NeetCodeIO
Minimum Score of a Path Between Two Cities - Leetcode 2492 - Python
NeetCodeIO
Sqrt(x) - Leetcode 69 - Python
NeetCodeIO
Successful Pairs of Spells and Potions - Leetcode 2300 - Python
NeetCodeIO
Optimal Partition of String - Leetcode 2405 - Python
NeetCodeIO
More on: Systems Design Basics
View skill →Related Reads
📰
📰
📰
📰
Building a Power Grid Inside Minecraft with BFS Algorithms
Dev.to · Carlos Cortez 🇵🇪 [AWS Hero]
The Run-Length Encoding Trick: How Simple Strings Get Compressed
Medium · Programming
75 Days of Leetcode — Day 4: #238 — Product of Array Except Self
Medium · AI
I implemented the algorithm that broke the sorting barrier. Dijkstra still wins.
Medium · Programming
Chapters (4)
Read the problem
1:14
Read example
2:15
Solution One
11:55
Solution Two
🎓
Tutor Explanation
DeepCamp AI