Javascript is synchronous. Things run one after the other without waiting for previous procedures to change.
let name;
name = 'bob'
console.log(name) // bob
name = 'alice'
console.log(name) // alice
As we would expect, each line above runs one after the other. However, there are many cases where this doesn't help us. For example:
let name;
name = 'bob';
console.log(name); // bob
setTimeout(function(){
name = 'alice';
},100)
console.log(name); // ?
Above we are setting the name to be changed after 100 milliseconds. However, the second log doesn't wait for the change, and so both console logs of name
will return "bob". (The name does change but not fast enough.)
solution without a callback
The example we are working with is pretty simple for now, we could easily move the second console log inside the timer and have it done, but let's use a callback function instead.
let name;
const callback = () => console.log(name)
name = 'bob'
console.log(name); // bob
setTimeout(function(){
name = 'alice';
callback(); // alice
},100)
Now we are accounting for the delay by adding our console log inside a callback function and letting the setTimeout
run the callback in 100 milliseconds.
Promises remove the need for a callback
In plain English, when you make a promise you are saying that you will do something at a given time (when conditions are right). You could say, the timer promises that in 100 milliseconds it will change the name to "alice". Then, when the timer fulfills the promise it's up to us to do something upon the fulfillment. So it's not that different from a callback but it doesn't need to know what happens afterward.
Let's see it in action
let name;
name = 'bob'
console.log(name); // bob
const ourPromise = new Promise((resolve, reject)=>{
setTimeout(function(){
name = 'alice';
resolve();
},100)
})
ourPromise.then(() => console.log(name))
We wrap our setTimeout
in a promise and as such the setTimeout
doesn't need to know what we will do with the changes it created. The execution of resolve()
simply changes the state of the promise from pending
to fulfilled
, then it's up to use to listen for that change at any point by running the then()
method on the promise
Note: if you run console.log(ourPromise)
inside the .then()
method you'll get an object of { <state>: "fulfilled", <value>: undefined }
. If you remove resolve()
from the promise and run the console log again inside then()
, you'll get { <state>: "pending" }
ourPromise.then(() => console.log(ourPromise))
Scope
As you have seen from the console log when the promise is resolved it usually would deliver some data. In our example, we wanted to test whether the name
from our global scope could be changed inside a promise. Pointless in the real world but easy for the sake of demonstration.
Let's see how we could use the internal scope of the promise instead:
let name;
name = 'bob'
console.log(name); // bob
const ourPromise = new Promise((resolve, reject)=>{
let name; //different to the outside scope
setTimeout(function(){
name = 'alice';
resolve(name); // pass this new name
},100)
})
ourPromise.then(resName => console.log(`promised name is ${resName}, and not ${name}`))
console.log(name); // we no longer need this (it still return 'bob'
Inside the promise, we created a new "name" variable (let name
) which is completely different from the "name" in the outer scope. Then we change it after 100 milliseconds and this time we pass it with the resolve method.
Note: if we re-try to run the console log inside the then
method this time we'll get a value { <state>: "fulfilled", <value>: "alice" }
. And in most of the time we would we waiting for a value to come from some api. For example:
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(resolvedData => console.log(resolvedData))
Top comments (0)