ThIs video is actually insane. The level of JS mastery it takes to explain all of this in thorough detail, code up the project without any major errors or missteps, and remember all of intricate implementation logic on one of the most complicated topics in JS is very impressive.
This is cool! One very small suggestion: 18:58 I would write `value && (typeof value.then) === 'function'` instead of the `instanceof` check. That way you can mix and match your promises with the normal Promises and even other implementations
Kyle, this video is awesome, and I learned a lot from this one!! I have some minor considerations to make, as this subject is somewhat complex, but overall your content is so rich, you did a great job! #1: At 2:36, the executor function (referred as cb in the video) is called syncronously as soon as the constructor is invoked. If an error is thrown during its execution and if the promise is still pending, then it will be rejected as expected with that error as a reason. But, if the promise has already been settled (either fulfilled or rejected), and an error is thrown during the execution of cb, this error will be ignored and can not be caught (not even if you wrap the code within a try-catch). In this case, trying to reject the promise in the catch block with the captured error won't work, as it has already been fulfilled (or rejected for some other reason). Example code: const p = new Promise((resolve, reject) => { resolve('Success') /* simulate a syncronous error after calling resolve */ throw new Error('I can not be caught') /* even if you try to call reject in a catch block, this promise has already been fulfilled */ }) I'm not sure if it is intended by the promise API implementation, but that's what happens.. #2: At 20:28. The resolution and rejection of a promise are made syncronously inside the executor. The evidence is that when you log a promise right after resolving it directly, it is not in a pending state: const p = new Promise((resolve, reject) => resolve('Success')); console.log(p) // Promise fulfilled, not pending (It would be pending if the resolution, i.e. calling resolve, were asyncronous) However, callbacks attached to the promise through .then, .catch and .finally are scheduled to run asyncronously when they are eventually called: p.then((value) => console.log(`This runs asyncronously. Value: ${value}`) // non-blocking code, as .then immediately returns a promise So, in this.#onSuccess and this.#onFail, you should wrap only the this.#runCallbacks call and an eventual uncaught error to be thrown within the queueMicrotask() (callbacks run and unhandled errors are thrown asyncronously), but not the portion of the function body where you set the value and the state of the promise, as this part is syncronous, and not scheduled to the queue microtask: #onSuccess(value) { // outside queueMicrotask this.state = STATE.FULFILLED this.value = value // the following is asyncronous queueMicrotask(() => { this.#runCallbacks() // ... // unhandled errors, i.e., without corresponding catchCb handlers registered, should be thrown here as well } }
It's the mark of a good programmer I'd say. Lots of concepts borrow meaning and words from the real world. When you really start thinking about programming, its problems, and how to solve them; linguistics become increasingly more important the more complex it gets. The heuristics of language apply to programming, because we need to verbalise our problems in order to convert the solutions to code syntax. I've found myself becoming fond of linguistics by figuring out why e.g a packet, class, or function is called what they are. But the words themselves are just that, a means of handling information/specification, the exact same as coding. Even processor is a funny one to think about. Atoms are a thing, factories too. But describing these in English is vastly different from in let's say C++, but the concepts are enough alike you get the gist without the code.
I thought I have some good knowledges about promises but after this video my mind is stunned and I feel like I am a worthless microbial life form. I guess I will have to go over the code many times before even a single light goes on. But any way that was an epic efforts on your part, so I Thank you.
Wow! I watched this at 1.5x speed, and really learned a lot from it. The way each part of the code just says "hey buddy, can you fo this for me?" Is so amazing. A good programmer teaches the different parts of their code to work as an effective and cohesive team!
28:22 you want to shallow clone the passed in `promises` array or store the length value separately, because this is subject to manipulating the passed in array after it's been passed into Promise.all and it could "complete early" or "never complete" because the length would not match the amount of completedPromises count. Test case 1 ``` const waitP = new MyPromise(r => setTimeout(r, 2000)); const arr = [waitP]; MyPromise.all(arr).then(() => console.log('I am finished'), () => console.log('I failed')); arr.push(0); ``` Test case 2 ``` const waitP = new MyPromise(r => setTimeout(r, 2000)); const longWaitP = new MyPromise(r => setTimeout(r, 20000000)); const arr = [waitP, longWaitP]; MyPromise.all(arr).then(() => console.log('I am finished WAY before 5 hours'), () => console.log('I failed')); arr.length = 1; ```
Omg... Lately I've been trying hard just to be able to watch a 6 hours tutorial which it took SSSSSSOOOOOOOOOOOOOOOOOOOO MUCH metaphors, daily life examples, bla blas for the tutor to get to the damn point. Thanks for this clean video.
Very nice lecture! One bug I could spot - if the MyPromise is already settled new handlers attached with '.then()' will fire synchronously, while in native Promise they always fire async. The fix is trivial though - just wrap the call to 'this.#runCallbacks()' inside 'then' with `queueMicrotask`.
Yeah i am not sure why is there 'queueMicrotask' in #onSuccess and in #onFailer? Shouldn't first promise created by constructor be resolved synchronously, immediately? Should 'queueMicrotask' only exist somewhere in then()?
@@neoruss3553 Nope, one of key designs in Promises is that they always resolved async. The point is to give them determenistic behaviour rather than trying to guess/figure out whether they resolve sync or async which was the problem with callback pattern
@@orbry Sorry to annoy you with this, this is not definately the place for asking people about this stuff and i don't except you to answer. This is whats confusing me: If we use built in promise constructor: const Promise1 = new Promise(resolve => resolve(5)) and console log it immediately: console.log(Promise1) we get: Promise{fulfilled, value} before any other code in script runs. Which means promise resolved immediately. If resolve method(in this case #onSuccess) has queueMicrotask() that means setting state and value should be done after the script completes and console.log(Promise1) shoud show: Promise{} because changing state and value is pushed to be done on microtask queue?
@@SahraClayton The optional chaining operator. For example, obj.val?.prop will be undefined if "obj" does not have the property "val". Without optional chaining, this would result in an Uncaught TypeError. This works for functions too: obj.myFunction?.()
Spent more than a day seraching to see how promises (especially .then()) work under the hood. I found other implementations but they were to complex compared to yours. Could'nt believe you also cover advance topics like this one!
JS Promises are tricky for beginners, this exercise might over complicate thing for newbies to Promises so tread carefully. For interviews I would focus more on understanding the fundamentals of promises, chaining, and async/await. Also understanding synchronous vs. asynchronous code. Though I’m not a big fan of this particular video, Kyle is one of the best teachers of JS Promises on this entire platform.
It is common practice to get asked to implement JavaScript features, or any other language feature, as part of an interview or take home assignment. You may not get asked to implement the full feature, and instead may just get asked to talk about your thought process behind how you would implement or plan out the feature. Implementing promises is for sure one of the harder JS features to implement and likely not to be asked in an interview but if you can do this you can implement pretty much any feature you could think of. This tutorial is also not meant to be a tutorial on promises. I already have a concise tutorial on promises that is focused on being simple. This is supposed to be a difficult and challenging problem for intermediate to advanced developers. I am not trying to make it any more difficult than it needs to be. This is just a very tricky feature to implement correctly.
19:26 A more correct way is checking if the property 'then' is inside the passed value. This is what the native promise api does and allows compatibility with other promise libraries
Interesting video! For the static resolve and reject methods you use new Promise() in stead of new MyPromise(). Both will of course work, but it makes my eye twitch a little :D
22:49 you forgot one part of the finally handling. If the finally function returns a promiselike, it awaits the result. If the finally handler returns a rejected promise, it causes the chained promise to be rejected, rather than a promise with the result before
10:30 I think I understand everything up to this point, but I get lost here. I don't see why you would want to run the callbacks from within the "then" function. Additionally, since the "runCallbacks" function will clear the "thenCbs" array, why does "thenCbs" need to be an array, since it will never contain more than one entry?
You've a good point there! I think what you got wrong is that the runCallbacks function doesn't _always_ run the callbacks and clear the array. Think of a Promise that is not resolved immediately - you can call .then() multiple times on the same instance (I'm not talking about chaining), while it is still in the "pending" state.
And regarding your first question: Think of a promise that _is_ immediately resolved (perhaps right upon creation) - the first time .then() is called, you would want the callback to be executed as soon as you call .then
Very impressive programming skills. But as a suggestion, there are a lot of behind-the-scenes concepts regarding promises that the viewer needs to know for all this to make sense. For ex, submitting onSuccess and onFail to the micro task queue presupposes that the viewer is familiar with the micro task loop (vs the macro task loop); so, it might be helpful to know that that queueMicrotask does this... This could easily be a much longer video if you added some conceptual overview that is more detailed than your other video on promises. Otherwise, I think most folks may know how to code this up, but they may not know why...just sayin...
Great video and explanations! 👍 But shouldn't perhaps the resolve and reject static methods be defined within the MyPromise class, instead of falling back on the Promise class itself if the goal is to replicate the Promise functionality without relying on it?
@@WebDevSimplified Thank you for the clarification! I also saw that you are using the ordinary equality operators. Are there any specific reasons for preferring them to the strict equality operators? p.s.: I really liked the fact that you used the new private fields, available with the new specification that are now part of JS itself (in contrast to private TS fields which are there during development, but get erased after transpilation).
Very nice explained, thanks a lot! Could you make a video showing how to properly extend the Promise class? It would be very handy to have a Promise that can be resolved or rejected externally by adding .resolve() and .reject() to the created instance. It would also be great to define a timeout which rejects the promise if it is not fulfilled in time.
I appreciate the effort you put into your programming tutorials it's clear you know your stuff. However, it would be really helpful if you could slow down the pace a bit and limit the scrolling during your explanations. It's quite challenging to follow along with the rapid movements. A clearer, step-by-step walkthrough would enhance understanding and allow viewers like me to really grasp the concepts you're presenting.
I am not sure why is there 'queueMicrotask' in #onSuccess and in #onFailer? Shouldn't first promise created by constructor be resolved synchronously, immediately? Should 'queueMicrotask' only exist somewhere in then()?
Yes, resolution and rejection are syncronous operations. Only ```this.#runCallbacks()``` and ```throw new UncaughtPromiseError()``` should be wrapped within queueMicrotask, as these are asyncronous.
Hey thanks for the video but I would say you were going a little bit fast considering some stuff was really complicated (especially chaining part). Would be nice to move on a little bit slower pace and have more explanation on them :)
in runCallbacks how do you handle calling the thenCbs but the first cb throw an error , wont you stop clearing the array or not call the rest of the cbs. also can the cb function itself adds more then then cb to the promise itself
Errors thrown while running callbacks happen after the promise has already been settled (either resolved or rejected with some other error), so these can not be captured with a .catch and won't affect the promise state. Those errors, if not handled, will propagate from the execution context of the callback that throws. What you could do to solve this is wrapping the callback body within a try-catch and handling the error in the callback implementation itself..
Never knew queueMicrotask existed. Interesting stuff. Id like to see async await implementation, but I guess it is impossible to do in js without transpiling?
Async await will work fine with this. *await* just looks for a *then method* and call it to get the value. if it doesn't find any it will return the data itself.
Great video! Personally I feel that doing this is the only way I can truly understand what promises are. I mean sure I can use them, but after this I understand what they are, which is important.
Can you please explain why and on what occasions the 'this' would not be properly bound? As in, why do we need to separately bind the success and fail callbacks?
@@bardhan.abhirup chatgpt gave me this Here are a few scenarios where bind is often necessary: Callbacks and Event Handlers: If you pass class methods as callbacks or event handlers to external functions or libraries, you may need to bind them to ensure that this refers to the class instance. Asynchronous Operations: If your methods are involved in asynchronous operations (e.g., Promise callbacks, setTimeout, event listeners), the context of this might change when the callback is executed. Binding can help ensure the correct context. Passing Methods as Arguments: If you pass class methods as arguments to other functions, and these methods rely on the class instance (this), binding is often necessary.
The uncaught promise error logic is incorrect. Consider this example: new MyPromise((resolve) => { setTimeout(() => { resolve(420); }, 1000); }) .then((value) => { return new MyPromise((resolve, reject) => { reject(value); }); }) .catch((value) => value) .then((value) => { console.log(value); }); This should log 420, but instead throws an uncaught promise error. The reason this happens is because we return a promise in the first .then() which immediately rejects. While the rejection should be caught by the .catch() right after, it doesn't, because the callback gets called before the .catch() is attached in the first place, resulting in an uncaught promise error.
nice catch! This promise implementation is definitely a very intense topic in js. we need check to see if the register thenCb returns MyPromise let res = thenCb(result); if (res instanceof MyPromise) { res .then((newResult) => { resolve(newResult); }) .catch((error) => reject(error)); } else { resolve(res); }
Wow many people spamming "return this instead of a new promise" really sums up that promise is a tough Js topic and many people don't really get it how it works to the core, they just like to pretend that they do.
It is more complicated than that. The test in the video are not complete. The code in the video is open to vulnerabilities too. If you want the real challenge : github.com/promises-aplus/promises-tests
*then* and *catch* returns a *Promise* that resolves to the value returned by their callbacks, returning the same *Promise* you will get the old value.
Hi Kyle, In MyPromiseTest.js line 65: const checkFunc = v => v => expect(v).toBeUndefined() Should it be "const checkFunc = v => expect(v).toBeUndefined()" ? Otherwise the checkFunc when run as callback is returning another arrow function instead of executing the spec expectation ?
whenever i learn a new concept i tried out a bunch of different things.....just found out that if you put the catch block before the then block, and if the promise is rejected then the execution will go through then block. just asking you if there is any useCase of this type of conditions.
I feel dumb considering I’ve basically used all these methods in building classes that handle custom event assigning yet The standard for dealing with promises seems so foreign I didn’t even realize it could be implemented with pure JavaScript
Actually, ```undefined == null``` returns true (loose equality), both are nullish values in JavaScript. On the other hand, ```undefined === null``` returns false (strict equality).
This is an interview problem. The interviewer will ask you to create your own implementation of Promise. This is how you do it. Not necessary to know this unless you are interviewing. In real world scenario you will never have to write your own Promise. Because it’s already in JavaScript.
There is this "Promise" class in Javascript, which is _super_ useful. You can find a link in the video description to learn about it. The objective of this video is to create a class that does the same as the "Promise" class that is provided by Javascript - as an exercise and because it's interesting.
@@DaaWood998 senior frontend roles I guess. Because in senior backend roles they ask system design problems mostly. In senior frontend roles you need to have a deeper understanding of core JS and other concepts regardless of which library/framework you use for work.
Why return a new promise from then Instead of just returning the same promise? Just keep returning this. Also, isn't it more appropriate for the "run callbacks" to use a while loop and shift the call acks? If the callbacks themselves involve some async stuff, new callbacks could be added while those callbacks are running. The forEach (or a more appropriate Reduce) would cause any callbacks added during that time to be lost.
.then would return itself, not a new promise. Take this example: write.then(doSomething).catch(oops). If .then returned a new promise, the .catch wouldn't be attached to the state of "write" and we'd probably be "oops"ing an error in doSomething instead
As for the while loop thing, I would personally just have .then/.catch check to see if the state is fulfilled/rejected. If it is, then either run the callback right there or throw it away since it'll never be able to run.
@@JaekSean Nope, that's not how chain of promises work. All of the methods [then, catch, finally] return a new Promise, the trick with your "write" example is that if you do not explicitely attach second handler in ".then" it will be substituted with identitiy function which just rethrows the error effectively propagating it down the chain.
I'm sorry, could somebody explain me, how the resolved value from the first promise gets into the new one within `then` method? As far as I can understand, it is because we bind `onSuccess` to `this` and thus `onSuccess` method now references to the previous promise?
then(thenCb, catchCb) { return new MyPromise((resolve, reject) => { this.#thenCbs.push(result => { if (thenCb == null) { resolve(result) return } try { resolve(thenCb(result)) } catch (error) { reject(error) } }) ....etc 1. The thenCb and catchCb are pushed to the callback arrays of the Promise object that "then" is invoked on, ie. the so-called previous promise. 2. The "resolve/reject" callbacks in the new Promise are bound to the new Promise object. These resolve/reject are used in the callback arrays of the "previous promise" in the form of "resolve(thenCb(result))", meaning when the previous promise runs the callback, it uses the resolve function bound to the new promise to write out the result to the new promise.
Someone asked me once how to make a variable created using var to throw an error when it is reassigned. This was to be done using prototype. Do you know how to do it?
Honestly I don't believe its possible to stop a variable from being reassigned if var is used instead of const, however you mentioned prototypes which concern objects and not variable reassignment. It is possible to disable objects from being modified and trying to do so would result in errors being thrown, see object property descriptors for more info.
@@UnifiedFriends I mean primitives don't have prototypes but yeah i get your point. what do you mean with initalization prototype? never heard of that term before. Could you explain how you would be able to disabled the ability for a variable declared with var to not be reassigned? I really can't see any possible way.
There is no way to prevent a variable declared with var from being reassigned. However object properties can be locked which is what I'm thinking that is the question here. 'use strict' // needed to get the error thrown, otherwise an attempt to reassign property will quietly fail var obj = { prop: 'this cannot be changed' } Object.freeze(obj) // or instead of Object.freeze() Object.defineProperty(obj, 'prop', { writable: false }) obj.prop = 'this will throw an error.' // TypeError: Cannot assign to read only property 'prop' of object '#'
Thanks for the reply, object.freeze does sound like something would have expected I suppose. I too think it's not possible to do it for a normal variable.
@@gajrajsingh51 even then, object.freeze is a "shallow freeze", if the object has inner objects in it the inner objects are not frozen, you would have to loop through all the inner objects recursively to freeze them
15:19 in an Array's .push() method how can you get "result" variable???? Makes no sense. The Array's push method just takes value as an argument not callback function. What are you doing?
If someone would be nice enough to answer some of my questions, I would be much apreciated! 1. In the then() method, why does the coder call the #runCallbacks()? I would think that, since the onFailed and onSuccess method queues a microtask and calls the stored callbacks in the thenCbs or the catchCbs array respectively, why not just let the microtask do the job? 2. Also in the then method, why not just push the callbacks from then and catch to its respective array? Why bother pushing a new function? 3. I do not know if the code actually handles already-resolved promise. Does it call the callbacks passed to then immidiately? Again, if anyone can help me understand promises better by answering these questions, I would be very glad.
Yeah, what *await* actually does is look for a *then method* if it finds a it, it will call it to get the value else it *returns* the value itself. So actually the only way to *resolve* a *Promise* is by calling the *then method* . If you remove it from the *Promise* , *await* won't get its value.
@@Laura69 Yeah I just tested it, it works. This is cool! Regarding your second remark: Yes you are right if you are talking about getting the value. But the way you phrased it is a little incorrect - Promises resolve on their own, regardless of whether you call .then() or not (or await them).
@@soniablanche5672 Yeah I know that, but obviously that's not the important thing about *await* , you want to resolve with it the value that the object represents (Promise, thenable objects), not the object itself.
@@Proth7 If you ask why we need binding this on success and fail then let me explain it shortly. As you can see, onSuccess and onFail methods also uses "this" inside of themselves to call other sibling methods. However the issue is when you provide these onSuccess and onFailure methods to another function then, when that function is being called, then the context of "this" won't be myPromise but the caller function.
Kyle, can you please go slow a little bit. It's kind of hard to catch you for a non english-speaker. Yeah I know I can decrease the playback speed but still please keep it a lil bit slow. The content is great, you will learn alot but The pace of cotent delivery is super irritating.
I do a lot of programming in JavaScript, but you make me feel like a newbie... My compliments on your excellent content and clear explanation!
ThIs video is actually insane. The level of JS mastery it takes to explain all of this in thorough detail, code up the project without any major errors or missteps, and remember all of intricate implementation logic on one of the most complicated topics in JS is very impressive.
This is cool!
One very small suggestion: 18:58 I would write `value && (typeof value.then) === 'function'` instead of the `instanceof` check. That way you can mix and match your promises with the normal Promises and even other implementations
Also known as 'Thenables'
Kyle, this video is awesome, and I learned a lot from this one!! I have some minor considerations to make, as this subject is somewhat complex, but overall your content is so rich, you did a great job!
#1: At 2:36, the executor function (referred as cb in the video) is called syncronously as soon as the constructor is invoked. If an error is thrown during its execution and if the promise is still pending, then it will be rejected as expected with that error as a reason. But, if the promise has already been settled (either fulfilled or rejected), and an error is thrown during the execution of cb, this error will be ignored and can not be caught (not even if you wrap the code within a try-catch). In this case, trying to reject the promise in the catch block with the captured error won't work, as it has already been fulfilled (or rejected for some other reason). Example code:
const p = new Promise((resolve, reject) => {
resolve('Success')
/* simulate a syncronous error after calling resolve */
throw new Error('I can not be caught')
/* even if you try to call reject in a catch block, this promise has already been fulfilled */
})
I'm not sure if it is intended by the promise API implementation, but that's what happens..
#2: At 20:28. The resolution and rejection of a promise are made syncronously inside the executor. The evidence is that when you log a promise right after resolving it directly, it is not in a pending state:
const p = new Promise((resolve, reject) => resolve('Success'));
console.log(p) // Promise fulfilled, not pending
(It would be pending if the resolution, i.e. calling resolve, were asyncronous)
However, callbacks attached to the promise through .then, .catch and .finally are scheduled to run asyncronously when they are eventually called:
p.then((value) => console.log(`This runs asyncronously. Value: ${value}`)
// non-blocking code, as .then immediately returns a promise
So, in this.#onSuccess and this.#onFail, you should wrap only the this.#runCallbacks call and an eventual uncaught error to be thrown within the queueMicrotask() (callbacks run and unhandled errors are thrown asyncronously), but not the portion of the function body where you set the value and the state of the promise, as this part is syncronous, and not scheduled to the queue microtask:
#onSuccess(value) {
// outside queueMicrotask
this.state = STATE.FULFILLED
this.value = value
// the following is asyncronous
queueMicrotask(() => {
this.#runCallbacks()
// ...
// unhandled errors, i.e., without corresponding catchCb handlers registered, should be thrown here as well
}
}
This is an impressive video. Your ability to both think in code and verbalize it in english simultaneously is uncommon ime.
It's the mark of a good programmer I'd say. Lots of concepts borrow meaning and words from the real world. When you really start thinking about programming, its problems, and how to solve them; linguistics become increasingly more important the more complex it gets. The heuristics of language apply to programming, because we need to verbalise our problems in order to convert the solutions to code syntax.
I've found myself becoming fond of linguistics by figuring out why e.g a packet, class, or function is called what they are. But the words themselves are just that, a means of handling information/specification, the exact same as coding.
Even processor is a funny one to think about. Atoms are a thing, factories too. But describing these in English is vastly different from in let's say C++, but the concepts are enough alike you get the gist without the code.
by the way "async await" works with any object that has a ".then()" method, so you can use async await with a custom Promise class.
The async await returns a promise which is why you can use the .then(). It defeats the purpose of implementing your own promise.
I thought I have some good knowledges about promises but after this video my mind is stunned and I feel like I am a worthless microbial life form.
I guess I will have to go over the code many times before even a single light goes on.
But any way that was an epic efforts on your part, so I Thank you.
Wow! I watched this at 1.5x speed, and really learned a lot from it. The way each part of the code just says "hey buddy, can you fo this for me?" Is so amazing. A good programmer teaches the different parts of their code to work as an effective and cohesive team!
At 13:52, "bound" is the word you were looking for haha. Excellent video as usual Kyle!
28:22 you want to shallow clone the passed in `promises` array or store the length value separately, because this is subject to manipulating the passed in array after it's been passed into Promise.all and it could "complete early" or "never complete" because the length would not match the amount of completedPromises count.
Test case 1
```
const waitP = new MyPromise(r => setTimeout(r, 2000));
const arr = [waitP];
MyPromise.all(arr).then(() => console.log('I am finished'), () => console.log('I failed'));
arr.push(0);
```
Test case 2
```
const waitP = new MyPromise(r => setTimeout(r, 2000));
const longWaitP = new MyPromise(r => setTimeout(r, 20000000));
const arr = [waitP, longWaitP];
MyPromise.all(arr).then(() => console.log('I am finished WAY before 5 hours'), () => console.log('I failed'));
arr.length = 1;
```
This was one of the most comprehensive explanations on what Promise methods do. Thanks for the video!
Omg... Lately I've been trying hard just to be able to watch a 6 hours tutorial which it took SSSSSSOOOOOOOOOOOOOOOOOOOO MUCH metaphors, daily life examples, bla blas for the tutor to get to the damn point.
Thanks for this clean video.
Very nice lecture!
One bug I could spot - if the MyPromise is already settled new handlers attached with '.then()' will fire synchronously, while in native Promise they always fire async. The fix is trivial though - just wrap the call to 'this.#runCallbacks()' inside 'then' with `queueMicrotask`.
Yeah i am not sure why is there 'queueMicrotask' in #onSuccess and in #onFailer? Shouldn't first promise created by constructor be resolved synchronously, immediately? Should 'queueMicrotask' only exist somewhere in then()?
@@neoruss3553 Nope, one of key designs in Promises is that they always resolved async. The point is to give them determenistic behaviour rather than trying to guess/figure out whether they resolve sync or async which was the problem with callback pattern
@@orbry Sorry to annoy you with this, this is not definately the place for asking people about this stuff and i don't except you to answer.
This is whats confusing me:
If we use built in promise constructor:
const Promise1 = new Promise(resolve => resolve(5))
and console log it immediately:
console.log(Promise1)
we get: Promise{fulfilled, value} before any other code in script runs. Which means promise resolved immediately.
If resolve method(in this case #onSuccess) has queueMicrotask() that means setting state and value should be done after the script completes and
console.log(Promise1)
shoud show: Promise{} because changing state and value is pushed to be done on microtask queue?
Your video put a whole new meaning to SIMPLIFIED, if you can please do a video on intersectionobserver polyfill too. THANKS
read the docs
@@jww0007 I have, just like Promises but i learnt something new here called "queuemicrotask", not using it in any of my projects but its fun to know
At 25:26 shouldn’t resolve and reject methods return MyPromise objects instead of original Promises?
Whoops. You are correct.
@@WebDevSimplified I have a question, can you tell me what '?' after a word means. For example user?
@@WebDevSimplified Thanks for responding to this sir! (i'm not OP but still grateful)
@@SahraClayton The optional chaining operator. For example, obj.val?.prop will be undefined if "obj" does not have the property "val". Without optional chaining, this would result in an Uncaught TypeError. This works for functions too: obj.myFunction?.()
@@JonathanMorrisMusic Thank you
This was really interesting. I'd never thought about the implementation behind promises
Cool!!! I‘m trying to find some resources introduce Promise implementation. I believe your video is the best!!
Spent more than a day seraching to see how promises (especially .then()) work under the hood. I found other implementations but they were to complex compared to yours. Could'nt believe you also cover advance topics like this one!
JS Promises are tricky for beginners, this exercise might over complicate thing for newbies to Promises so tread carefully. For interviews I would focus more on understanding the fundamentals of promises, chaining, and async/await. Also understanding synchronous vs. asynchronous code.
Though I’m not a big fan of this particular video, Kyle is one of the best teachers of JS Promises on this entire platform.
It is common practice to get asked to implement JavaScript features, or any other language feature, as part of an interview or take home assignment. You may not get asked to implement the full feature, and instead may just get asked to talk about your thought process behind how you would implement or plan out the feature. Implementing promises is for sure one of the harder JS features to implement and likely not to be asked in an interview but if you can do this you can implement pretty much any feature you could think of.
This tutorial is also not meant to be a tutorial on promises. I already have a concise tutorial on promises that is focused on being simple. This is supposed to be a difficult and challenging problem for intermediate to advanced developers. I am not trying to make it any more difficult than it needs to be. This is just a very tricky feature to implement correctly.
@@WebDevSimplified 👌🏼👍🏼
19:26 A more correct way is checking if the property 'then' is inside the passed value. This is what the native promise api does and allows compatibility with other promise libraries
I did this in Java back in the early GWT days.
(Yes, I know Java has equivalents now, but not at the time)
The best Pormises related tutorial I have ever seen.
I can' believe I found this content and I was already subscribed to you lol. Lucky day
this video helped me finally understand wtf promises are doing. thanks!
Heey, Kyle have you ever thought about launching a podcast. For sure that I would listen to it. BTW, Great job with promises💫
Interesting video! For the static resolve and reject methods you use new Promise() in stead of new MyPromise(). Both will of course work, but it makes my eye twitch a little :D
So that is a typo?
Low key rockin' the Jimmy Neutron on top! 0:02
22:49 you forgot one part of the finally handling. If the finally function returns a promiselike, it awaits the result. If the finally handler returns a rejected promise, it causes the chained promise to be rejected, rather than a promise with the result before
After watching this video, I'm questioning my js knowledge. Learned lotta new stuff
wow, the best video for promise in depth
太牛逼了,这是我见过质量比较高的资料了,感谢分享。
10:30 I think I understand everything up to this point, but I get lost here. I don't see why you would want to run the callbacks from within the "then" function. Additionally, since the "runCallbacks" function will clear the "thenCbs" array, why does "thenCbs" need to be an array, since it will never contain more than one entry?
You've a good point there!
I think what you got wrong is that the runCallbacks function doesn't _always_ run the callbacks and clear the array. Think of a Promise that is not resolved immediately - you can call .then() multiple times on the same instance (I'm not talking about chaining), while it is still in the "pending" state.
And regarding your first question: Think of a promise that _is_ immediately resolved (perhaps right upon creation) - the first time .then() is called, you would want the callback to be executed as soon as you call .then
Well, theoretically you might add more thens before the initial resolves...
Very impressive programming skills. But as a suggestion, there are a lot of behind-the-scenes concepts regarding promises that the viewer needs to know for all this to make sense. For ex, submitting onSuccess and onFail to the micro task queue presupposes that the viewer is familiar with the micro task loop (vs the macro task loop); so, it might be helpful to know that that queueMicrotask does this...
This could easily be a much longer video if you added some conceptual overview that is more detailed than your other video on promises. Otherwise, I think most folks may know how to code this up, but they may not know why...just sayin...
16min in and my head hurts.
Great video and explanations! 👍
But shouldn't perhaps the resolve and reject static methods be defined within the MyPromise class, instead of falling back on the Promise class itself if the goal is to replicate the Promise functionality without relying on it?
This was just a typo on my end. If you replace the word Promise with MyPromise it will work.
@@WebDevSimplified Thank you for the clarification!
I also saw that you are using the ordinary equality operators. Are there any specific reasons for preferring them to the strict equality operators?
p.s.: I really liked the fact that you used the new private fields, available with the new specification that are now part of JS itself (in contrast to private TS fields which are there during development, but get erased after transpilation).
Awesome video! Do you have a video that shows how to build tests like the one you used on this video?
Very nice explained, thanks a lot!
Could you make a video showing how to properly extend the Promise class? It would be very handy to have a Promise that can be resolved or rejected externally by adding .resolve() and .reject() to the created instance. It would also be great to define a timeout which rejects the promise if it is not fulfilled in time.
I appreciate the effort you put into your programming tutorials it's clear you know your stuff. However, it would be really helpful if you could slow down the pace a bit and limit the scrolling during your explanations. It's quite challenging to follow along with the rapid movements. A clearer, step-by-step walkthrough would enhance understanding and allow viewers like me to really grasp the concepts you're presenting.
You're great. But would you mind making a video on js event loop. It's very confusing
checkout ruclips.net/video/8aGhZQkoFbQ/видео.html
Here's a link to the best one ever : ruclips.net/video/8aGhZQkoFbQ/видео.html
@@bobarndt862 this video actually made me understand js, lol
Look everyone, Kyle is writing tests XD
Like we will ever write tests! XD XD
Isn't this a DevOps thing? I have no idea how I would use that knowledge in backend/frontend
This was a complicated topic. If anything it helped me understand promises way more.
This is exactly what I was looking for!
Can you make a video on how to create a popper js from the scratch for positioning elements?
I was asked this question in Walmart SDE 3 interview. I got to this video to me after failing in the interview last week.
I am not sure why is there 'queueMicrotask' in #onSuccess and in #onFailer? Shouldn't first promise created by constructor be resolved synchronously, immediately? Should 'queueMicrotask' only exist somewhere in then()?
Yes, resolution and rejection are syncronous operations. Only ```this.#runCallbacks()``` and ```throw new UncaughtPromiseError()``` should be wrapped within queueMicrotask, as these are asyncronous.
Hey thanks for the video but I would say you were going a little bit fast considering some stuff was really complicated (especially chaining part). Would be nice to move on a little bit slower pace and have more explanation on them :)
Yep. Way too fast.
Intersting approach. I would have taken a stacking approach to the then() implementation, but your solution is probably way more performant
Sadly it's not 100% standard compliant. There's vulnerablities too.
in runCallbacks how do you handle calling the thenCbs but the first cb throw an error , wont you stop clearing the array or not call the rest of the cbs. also can the cb function itself adds more then then cb to the promise itself
Errors thrown while running callbacks happen after the promise has already been settled (either resolved or rejected with some other error), so these can not be captured with a .catch and won't affect the promise state. Those errors, if not handled, will propagate from the execution context of the callback that throws. What you could do to solve this is wrapping the callback body within a try-catch and handling the error in the callback implementation itself..
thank you for this excellent video. how you did for the automatique testing on the right side?? 😊
integrated terminal with "jest --watchAll --noStackTrace" running
Never knew queueMicrotask existed. Interesting stuff. Id like to see async await implementation, but I guess it is impossible to do in js without transpiling?
Async await will work fine with this.
*await* just looks for a *then method* and call it to get the value.
if it doesn't find any it will return the data itself.
Great video!
Personally I feel that doing this is the only way I can truly understand what promises are. I mean sure I can use them, but after this I understand what they are, which is important.
Can you please explain why and on what occasions the 'this' would not be properly bound? As in, why do we need to separately bind the success and fail callbacks?
Was hoping for the same explanation
chatgpt it
@@artahir123found the answer?
@@bardhan.abhirup
chatgpt gave me this
Here are a few scenarios where bind is often necessary:
Callbacks and Event Handlers: If you pass class methods as callbacks or event handlers to external functions or libraries, you may need to bind them to ensure that this refers to the class instance.
Asynchronous Operations: If your methods are involved in asynchronous operations (e.g., Promise callbacks, setTimeout, event listeners), the context of this might change when the callback is executed. Binding can help ensure the correct context.
Passing Methods as Arguments: If you pass class methods as arguments to other functions, and these methods rely on the class instance (this), binding is often necessary.
why do u need an Array for catch callbacks? doesn't catch only runs once when error occurs and the Promise goes into Rejected state?
The uncaught promise error logic is incorrect. Consider this example:
new MyPromise((resolve) => {
setTimeout(() => {
resolve(420);
}, 1000);
})
.then((value) => {
return new MyPromise((resolve, reject) => {
reject(value);
});
})
.catch((value) => value)
.then((value) => {
console.log(value);
});
This should log 420, but instead throws an uncaught promise error.
The reason this happens is because we return a promise in the first .then() which immediately rejects. While the rejection should be caught by the .catch() right after, it doesn't, because the callback gets called before the .catch() is attached in the first place, resulting in an uncaught promise error.
nice catch! This promise implementation is definitely a very intense topic in js.
we need check to see if the register thenCb returns MyPromise
let res = thenCb(result);
if (res instanceof MyPromise) {
res
.then((newResult) => {
resolve(newResult);
})
.catch((error) => reject(error));
} else {
resolve(res);
}
I noticed a minor issue on the static resolve and reject methods, you're calling Promise instead of MyPromise.
Yeah I was wondering same.
Thanks mate, very cool 👍
So cool. Well done. Easy to follow.
this video was hard to understand
Start creating Svelte videos!
Please add some videos for typescript generics
Wow many people spamming "return this instead of a new promise" really sums up that promise is a tough Js topic and many people don't really get it how it works to the core, they just like to pretend that they do.
returning this makes no sense, a promise can only store 1 value
Please make videos on Nest.js , Next.js , AWS, System design, Typescript, etc
I was asked to do this challenge it sounds more complicated than really is
It is more complicated than that. The test in the video are not complete. The code in the video is open to vulnerabilities too.
If you want the real challenge : github.com/promises-aplus/promises-tests
In your static resolve and reject methods, you used Promise instead of MyPromise.
For chaining why you are not return this (current instance of object) instead of creating new MyPromise?
*then* and *catch* returns a *Promise* that resolves to the value returned by their callbacks,
returning the same *Promise* you will get the old value.
Hi Kyle,
In MyPromiseTest.js line 65:
const checkFunc = v => v => expect(v).toBeUndefined()
Should it be "const checkFunc = v => expect(v).toBeUndefined()" ?
Otherwise the checkFunc when run as callback is returning another arrow function instead of executing the spec expectation ?
Fact: none of these comments have watched the full video.
The guy who watched in 2x speed: 🤡
@@hanyelchamon4569 Watched it 3.5x speed, that's about fine for me
@@hanyelchamon4569 i challenge thee.
Am still waiting for it to resolve
Fact: video doesn't actually implement asynchronous promises
whenever i learn a new concept i tried out a bunch of different things.....just found out that if you put the catch block before the then block, and if the promise is rejected then the execution will go through then block. just asking you if there is any useCase of this type of conditions.
This is fantastic!!!
5mins in and my head hurt too. 😂
It’s great video, but too hard for me to digest.
Web Dev please what JavaScript course can you suggest I should take to learn in depth JavaScript
I feel dumb considering I’ve basically used all these methods in building classes that handle custom event assigning yet The standard for dealing with promises seems so foreign I didn’t even realize it could be implemented with pure JavaScript
Heck you're good, Mr. Kyle :D
23:47 Is it guaranteed that _value_ is an error? 24:17
I didn't completely get it but hey, well don explanation
13:23 - undefined != null
undefined !== null
Actually, ```undefined == null``` returns true (loose equality), both are nullish values in JavaScript. On the other hand, ```undefined === null``` returns false (strict equality).
essentially promises are just a design pattern baked into the language. back in the days we just called it callback pattern.
It's not callback though, there is an inversion of control. Instead of giving a callback, you are given a callback resolver.
awesome
thx a lot of
Can anyone explain exactly what's the meaning of the title or whats the objective of this video please .I am not getting it.🙏
This is an interview problem. The interviewer will ask you to create your own implementation of Promise. This is how you do it. Not necessary to know this unless you are interviewing. In real world scenario you will never have to write your own Promise. Because it’s already in JavaScript.
There is this "Promise" class in Javascript, which is _super_ useful. You can find a link in the video description to learn about it.
The objective of this video is to create a class that does the same as the "Promise" class that is provided by Javascript - as an exercise and because it's interesting.
@@EdwinManual but at what level position would this kind of question be asked? And which stack?
@@DaaWood998 senior frontend roles I guess. Because in senior backend roles they ask system design problems mostly. In senior frontend roles you need to have a deeper understanding of core JS and other concepts regardless of which library/framework you use for work.
@@EdwinManual I hope i will understand the whole video one day .now it seems so complicated 🤔.I m begginer level aspiring to be a front-end developer.
Why return a new promise from then Instead of just returning the same promise? Just keep returning this.
Also, isn't it more appropriate for the "run callbacks" to use a while loop and shift the call acks? If the callbacks themselves involve some async stuff, new callbacks could be added while those callbacks are running.
The forEach (or a more appropriate Reduce) would cause any callbacks added during that time to be lost.
.then() returns a new promise, that's how it works with the real Promise
.then would return itself, not a new promise. Take this example: write.then(doSomething).catch(oops). If .then returned a new promise, the .catch wouldn't be attached to the state of "write" and we'd probably be "oops"ing an error in doSomething instead
As for the while loop thing, I would personally just have .then/.catch check to see if the state is fulfilled/rejected. If it is, then either run the callback right there or throw it away since it'll never be able to run.
@@JaekSean Nope, that's not how chain of promises work. All of the methods [then, catch, finally] return a new Promise, the trick with your "write" example is that if you do not explicitely attach second handler in ".then" it will be substituted with identitiy function which just rethrows the error effectively propagating it down the chain.
You misunderstand how async JS work. JS is single threaded. there is no way a callbacks will be "lost"
Do resolve and reject actually need the normal js promise, like new Promise()? Or is that a typo and was supposed to use new MyPromise()?
it's a typo, he said that somewhere down in the comment.
Amazing.
I'm sorry, could somebody explain me, how the resolved value from the first promise gets into the new one within `then` method? As far as I can understand, it is because we bind `onSuccess` to `this` and thus `onSuccess` method now references to the previous promise?
then(thenCb, catchCb) {
return new MyPromise((resolve, reject) => {
this.#thenCbs.push(result => {
if (thenCb == null) {
resolve(result)
return
}
try {
resolve(thenCb(result))
} catch (error) {
reject(error)
}
})
....etc
1. The thenCb and catchCb are pushed to the callback arrays of the Promise object that "then" is invoked on, ie. the so-called previous promise.
2. The "resolve/reject" callbacks in the new Promise are bound to the new Promise object. These resolve/reject are used in the callback arrays of the "previous promise" in the form of "resolve(thenCb(result))", meaning when the previous promise runs the callback, it uses the resolve function bound to the new promise to write out the result to the new promise.
Someone asked me once how to make a variable created using var to throw an error when it is reassigned. This was to be done using prototype. Do you know how to do it?
Honestly I don't believe its possible to stop a variable from being reassigned if var is used instead of const, however you mentioned prototypes which concern objects and not variable reassignment. It is possible to disable objects from being modified and trying to do so would result in errors being thrown, see object property descriptors for more info.
@@UnifiedFriends I mean primitives don't have prototypes but yeah i get your point. what do you mean with initalization prototype? never heard of that term before. Could you explain how you would be able to disabled the ability for a variable declared with var to not be reassigned? I really can't see any possible way.
There is no way to prevent a variable declared with var from being reassigned. However object properties can be locked which is what I'm thinking that is the question here.
'use strict' // needed to get the error thrown, otherwise an attempt to reassign property will quietly fail
var obj = { prop: 'this cannot be changed' }
Object.freeze(obj)
// or instead of Object.freeze()
Object.defineProperty(obj, 'prop', { writable: false })
obj.prop = 'this will throw an error.' // TypeError: Cannot assign to read only property 'prop' of object '#'
Thanks for the reply, object.freeze does sound like something would have expected I suppose. I too think it's not possible to do it for a normal variable.
@@gajrajsingh51 even then, object.freeze is a "shallow freeze", if the object has inner objects in it the inner objects are not frozen, you would have to loop through all the inner objects recursively to freeze them
You should make a music player 💪
Still just runs synchronously which defeats the whole purpose of Promises, other than a pseudo-Monad for some operation that might fail.
By queuing a micro task we are forcing the code to run async in the exact same way as promises. Promises use the same job queue as micro tasks.
15:19 in an Array's .push() method how can you get "result" variable???? Makes no sense. The Array's push method just takes value as an argument not callback function. What are you doing?
umm don't like using semi-colon ?
explain curryed(sum)(2)(5)() output=7
If someone would be nice enough to answer some of my questions, I would be much apreciated!
1. In the then() method, why does the coder call the #runCallbacks()? I would think that, since the onFailed and onSuccess method queues a microtask and calls the stored callbacks in the thenCbs or the catchCbs array respectively, why not just let the microtask do the job?
2. Also in the then method, why not just push the callbacks from then and catch to its respective array? Why bother pushing a new function?
3. I do not know if the code actually handles already-resolved promise. Does it call the callbacks passed to then immidiately?
Again, if anyone can help me understand promises better by answering these questions, I would be very glad.
Are these questions still actual for you?
Can you use await with this implementation?
Yeah, what *await* actually does is look for a *then method*
if it finds a it, it will call it to get the value
else it *returns* the value itself.
So actually the only way to *resolve* a *Promise* is by calling the *then method* .
If you remove it from the *Promise* , *await* won't get its value.
@@Laura69 Yeah I just tested it, it works. This is cool!
Regarding your second remark: Yes you are right if you are talking about getting the value. But the way you phrased it is a little incorrect - Promises resolve on their own, regardless of whether you call .then() or not (or await them).
@@dasten123 Well yeah, what I meant was
"the only way to get the value (error does not count) is by calling *then* .
@@Laura69 async await works with any value, even if it doesn't have a .then(), it will simply resolve immediately to the value that is being awaited
@@soniablanche5672 Yeah I know that,
but obviously that's not the important thing about *await* ,
you want to resolve with it the value that the object represents (Promise, thenable objects), not the object itself.
Istead of returning new MyPromise, return 'this' for chaining
That would only allow you to add .then/.catch onto the current promise. It wouldn't allow you to do promise chaining.
@@WebDevSimplified thanks for clearing up my doubt🙂
25:42 Isn't using javascript Promises within your own MyPromise implementation kind of defeating the purpose?
Tip: we can use arrow functions instead of binding this
Do you maybe know what's the issue here 13:18?
Do we loose the this context then? and that's why we need to bind "this"?
@@Proth7 If you ask why we need binding this on success and fail then let me explain it shortly. As you can see, onSuccess and onFail methods also uses "this" inside of themselves to call other sibling methods. However the issue is when you provide these onSuccess and onFailure methods to another function then, when that function is being called, then the context of "this" won't be myPromise but the caller function.
Cool
Wow
so if we don't like the native `Promise` behavior, we can create our own Promise!
Kyle, can you please go slow a little bit. It's kind of hard to catch you for a non english-speaker. Yeah I know I can decrease the playback speed but still please keep it a lil bit slow. The content is great, you will learn alot but The pace of cotent delivery is super irritating.
Coming from strongly typed languages JS is so freaking weird...
Please use Typescript at least 😅
Sure, boss.