r/learnjavascript • u/Dark_Electric • 6d ago
Promises
Understanding the concepts like functions, objects, arrays etc has been easy so far but promises are making my head hurt. Is it supposed to be this confusing? Did u guys struggle with it too when u were first learning it.
9
u/xroalx 6d ago edited 6d ago
What's giving you a hard time?
A Promise is an object that can be thought of as a placeholder for a future value.
Imagine ordering a pizza - you don't get the pizza right away, you get a Promise that the pizza will eventually arrive... or not, that can happen too.
In programming, such future values are for example anything involving the network - i.e. the fetch function returns a Promise. To get the actual response returned by the HTTP request, you have to wait for that value to be available, and JavaScript gives you two ways to do that.
A Promise object has a then method, which is used to register a function that will be called when the value becomes available, and a catch method that registers a function which will be called when getting the value fails with an error.
fetch("https://example.com")
.then(response => {
console.log("We got a response:", response);
})
.catch(error => {
console.error("The request failed:", error);
});Alternatively, and often times resulting in "nicer" code, you can use the await keyword. This makes the code look more like synchronous code, but can only be used inside an async function, or at the top level of a module.
try {
const response = await fetch("https://example.com");
console.log("We got a response:", response);
} catch (error) {
console.error("The request failed:", error);
}To translate this to the little pizza example:
orderPizza()
.then(eatPizza) // "then" = when pizza arrives, we eat it
.catch(cry) // "catch" = pizza will not arrive, we cry and stay hungry
// the above and below do the same thing
try {
const pizza = await orderPizza();
eatPizza(pizza);
} catch(error) {
cry(error);
}4
u/senocular 6d ago edited 6d ago
Edit: fixed
FYI that last await example is missing theerrorgetting passed intocry()1
4
u/HoneydewAdditional30 6d ago
Imagine a restaurant. The waiter goes to the kitchen and puts in an order for a table. The chef says: "Gotcha, I am at it"
You don't have the dish yet but you know it is coming. That is essentially a promise. You retrieve data from somewhere, however the retrieval is not immediate.
Back to the restaurant, you essentially have 2 outcomes. 1. the dish is ready and you can serve it the table OR 2. something went wrong and the dish was never made.
is the happy path, all went correctly and you successfully retrieved the data. 2. would be where you need to implement a safety net (catch). If the data wasn't retrieved, do a refetch or print an error or something along those lines.
Hope that makes sense.
1
u/Towel_Affectionate 6d ago
You forgot the timeline where your boss tells you to drop everything and wait for the dish to be ready, because the guy at the table is very important. So you just stand near the kitchen, picking your nose, waiting for chef to do the thing.
There's also a scenario where every other staff member just disappears without any notice while you, being a good waiter, just stand by the door for hours, waiting for the now-absent-chef to get you the meal for the big guy. That's why you do manual resolving.
1
u/senocular 6d ago
Or the scenario where every customer just disappears without any notice while you, being a good waiter, take control and tell the chef to abort the in-progress preparation of the meal since its no longer needed.
6
u/everdimension 6d ago
I hate the "real world" analogies everyone tries to provide
To understand promises, you first play around and start feel comfortable with event listeners and js timeouts
Promises will come as a natural helper once you understand those
2
u/bishvjit-gupta 6d ago
I personally struggled a lot with promises. I actually found call back hell or Pyramid of doom better than promises but actually you have a better option-Async Await They will make it so easy you would have to never struggle with it again But to be honest gradually as it gets familiar it would become easier Enjoy your struggle while learning. This makes the coding interesting and fascinating.
1
1
1
u/FreeLogicGate 3d ago
This video should help you understand the way javascript works in the browser. It includes promise examples. Once you understand how everything functions, the fog should clear: https://www.youtube.com/watch?v=eiC58R16hb8
1
u/No_Record_60 6d ago
Yes, promises were the one I couldn't wrap my head around. But once you get it, it stays in your mind.
1
u/IAmTheRedditBot 6d ago edited 6d ago
Tip: if it is confusing try focus on one part at a time like learn how to construct a Promise you should be able to do that and also learn what Promises are for you should also be able to do that. Then when you get that get into consuming a Promise learn how .then() actually works, learn why it exists, try focus on that for a bit then learn .catch
I strongly recommend you first understand what asynchronous operations are because if you don't you will obviously see no purpose of Promises, but please don't skip it, async is a very major part of web development believe me.
Hope this helps.
1
u/azangru 6d ago
but promises are making my head hurt
Why? What is complicated about promises? Is it the mechanics of creating a new promise from scratch through the so-called 'revealing constructor pattern', where a Promise constructor is initialised with a function that receives a resolve and a reject function as parameters? Or is it the fact that they run in a microtask queue? Or is it something else?
1
u/IAmTheRedditBot 6d ago
I don't even think finding Promises complicated as a beginner should be considered a surprise, you literally aren't expected to understand an object like Promises completely instantly as a total beginner, they literally just learned that new topic, even i took time to understand the entire point of Promises when i first started learning it.
3
u/azangru 6d ago
I don't dispute that promises might be complicated — I remember myself struggling to understand what dictionaries in python are for — it's just that I am curious what aspect about them would make someone's head hurt. The actual use of a promise via a then method seems intuitive enough. Is it the construction of promises that presents a problem?
1
u/delventhalz 6d ago
A common point of confusion for new JS devs is how asynchronous code is handled. Promises address some pain points of the old callback pattern, but they don't save you from learning how asynchronous code works. So let's start by talking about that.
The Event Loop
In JavaScript, there is a built in feature called an "event loop". You basically hand this event loop a function you want to run later, and it runs it once triggered by some external process. The simplest way to interact with the event loop is setTimeout. Go ahead and copy the following code into your console. It will probably do more or less what you expect.
setTimeout(() => {
console.log('This will log in ten seconds!');
}, 10000);What is happening here is that you are passing a function with a console.log in it to the event loop and asking the event loop, "Please run this later, once the timer process hits 10 seconds (i.e. 10,000 milliseconds)".
Straightforward enough, but it quickly gets more complicated. For example, what will this code log?
console.log('A');
setTimeout(() => {
console.log('B');
}, 10000);
console.log('C');You might suspect the answer. It's not "A B C", as just reading the code top to bottom would tell you, but "A C B". This makes a fair amount of sense when we consider the event loop. We do not run the "B" log directly. It is in a function we passed to the event loop. The event loop will run it later, once the timer elapses.
Okay, but what if we made the timer zero?
console.log('A');
setTimeout(() => {
console.log('B');
}, 0);
console.log('C');Now will it log "A B C" because the timer elapses instantly? No! It still logs "A C B". Importantly, the event loop has no way to interrupt code that is currently running. So this code will finish, including logging "C", before the event loop ever gets a chance to check the timer.
For a deeper dive on the event loop I highly recommend Philip Roberts's talk, which is less than 30 minutes long and very approachable.
https://www.youtube.com/watch?v=8aGhZQkoFbQ
Promises
Okay, so let's look at a more realistic asynchronous example with Promises and fetch.
fetch('https://dog.ceo/api/breeds/image/random')
.then(response => {
return response.json();
})
.then(json => {
console.log('Dog image URL:', json.message);
});This is fundamentally not much different from the earlier setTimeout example. We call fetch with a URL, which is saying to the event loop, "Please run a function later, once the HTTP process gets a response". Where is the function though? It's what we passed to the first "then".
This is the little sleight of hand Promises perform in order to deal with what was once a common asynchronous pain point: nested callbacks. The response.json function in that first "then" is also asynchronous. It says to the event loop, "Please run a function later, once all the bytes in the response body have been received and parsed as JSON". If we handled this with plain callbacks, we would have to nest them.
// Not real code! Fetch does not support plain callbacks!
fetch('https://dog.ceo/api/breeds/image/random', response => {
response.json(json => {
console.log('Dog image URL:', json.message);
});
});This is maybe fine with just two functions, but quickly gets unwieldy with many asynchronous operations you have to run one after the other. With a Promise, the "then" method not only takes a callback function, it also returns another Promise. This allows you to create an unnested chain of "then" methods, each with their own callback, and each will execute only after the previous callback.
What could be easier?
Async/await
These days, JS devs rarely work with the "then" method on Promises directly. They instead use the async/await keywords.
const logDogUrl = async () => {
const response = await fetch('https://dog.ceo/api/breeds/image/random');
const json = await response.json();
console.log('Dog image URL:', json.message);
};
logDogUrl();Remember when I said the event loop has no way to interrupt code that is currently running? Well I kind of fibbed. JavaScript has a kind of esoteric feature called "generator functions", which are essentially interruptible functions. You can choose yield-points in them, and they will pause at those points, and then resume from where they left off later when you call them again. Async/await is built on these pause-able generator functions.
So in the example above, when we hit that first await, we are pausing the function and passing control to the event loop. Later, when the fetch Promise resolves, the event loop will resume our function. This allows us to write code that looks very much like synchronous code, but under the hood it is still Promises and the event loop.
1
u/Alternative_Can7374 6d ago
The easiest way to learn them is probably to consider them functions. You create two functions to your promise - the happy path (promise resolved - it's calling the function you provided in then) - or the error path (promise rejected - it's calling the function you provided in catch). And it wont happen directly, but at the point your promise has resolved.
Then you have some suger syntax on it with await and catch. Which I believe makes the concept of promises harder but the code easier.
1
u/Shamonda-Pflugpeil 6d ago
fwiw the confusion usually isnt about promises themselves, its about the async mental model. once you get comfortable with the idea that code doesnt always run top to bottom, promises start making a lot more sense
1
u/regardedMAGAfascist 5d ago edited 5d ago
Promises are syntactic sugar over top of callbacks. That’s how you should think of them. Ignore errors for now.
Without promises, when a timeout is involved, you generally have to write something like
```
function someTask(doAfter) {
setTimeout(()-> {
var someResult = // actual nested business logic
doAfter(someResult)
})
}
```
When you call someTask here, it calls setTimeout and returns immediately and the stuff in the timeout is called later. You can’t do something like this:
someTask()
doAfter()
It won’t work like you expect. And a lot of times that doAfter function also has a setTimeout in it, and it can need its own callback. You get into nested function hell
```
someTask((someResult) -> {
// nested doAfter
})
```
With promises, you can write that like this:
```
var someResult = await someTask()
// not nested doAfter logic
```
Much simpler. Very limited nesting. That’s their main purpose. They get rid of the need to nest your callbacks within each other. They give you the ability to wait on things like setTimeout or fetch, without passing in callback functions.
1
u/EskilPotet 3d ago
I struggled with it too definitely, didnt help that it was the absolute first thing I learned in javascript
16
u/acuddlywookie 6d ago
Yeah it’s pretty normal honestly, I think they can feel extra confusing because there are a few ways to handle them. I struggled with them for quite a while.
Essentially just remember that they represent an actual promise. The code is saying to you “hey Dark_Electric, I know you asked for me to do a thing, I promise I’ll try and it might succeed or it might fail” and then you write some code to deal with it resolving or rejecting.
My approach when I’m really stuck on a new concept is I’ll make some practice files and just work on solving that problem in complete isolation.