What are Promises
JavaScript Promise is similar to how they mean in native English:
A promise/assurance that something will happen in the future, some value will return in the future, but not now
Imagine you are JavaScript, and your friend promises they will give you $5. You know your friend will give $5 sometime in the future, but not now, so you queue that task back into your memory, and continue doing your thing.
Why do we need Promises anyway?
Promise is part of the non-blocking asynchronous JavaScript workflow. Because JavaScript is single-threaded, we don't want certain actions to block the entire main thread. You don't want data fetching to block rendering the HTML. You don't want submitting requests to a server to pause the GIF playing in the background.
That's why JavaScript need a way to schedule some tasks that take time to the background and check back on them later, but not now. Prior to Promise, JavaScript uses callbacks, but that leads to huge problems of callback hell, so Promise is created as a more ergonomic solution.
Promises can be a chain
Imagine that immediately after your friend's promise, your mom demands $5 from you. Can you give it to her now? No you can't, because you don't have it, but you can schedule that: wait until after your friend give you $5, then you can give it to your mom. And that's how the Promises chain happens.
Friend gives you $5
<-- Then you give it to mom
<-- Then your mom praises you
<-- Then you are happy
Note that actions that depend on the value of a Promise get chained into a big Promise chain, and the whole thing becomes a big Promise. These scheduled things will happen sometime in the future, but not now. Once you are in Promise land, you can continue the chain, but can't go backout to the synchronous world.
Promises can have three states
Note that a Promise can have three states: Pending, Resolved, and Rejected. While waiting, the Promise is pending. If your friend's promise continues smoothly, the $5 is given successfully, then the Promise is resolved. However, if any errors happen in the Promise chain (your friend lost the money to the wind), it becomes Rejected, and will error out.
How to work with JS Promise & code samples
You can create a Promise yourself with the Promise constructor, which takes in a function that decide how to resolve or reject the promise.
const p = new Promise((resolve, reject) => {
const today = new Date();
// If at the time this Promise is invoked, it's Saturday
if (today.getDay() === 6) {
resolve("Happy day"); // Then successfully returns string
} else {
reject("Not Saturday") // Else error out
}
})
You can also quickly create a Promise that automatically resolves with Promise.resolve()
, or a Promise that automatically rejects with Promise.reject()
, which are mostly used for testing purposes.
Most of the time, you won't need to create a Promise yourself, and instead work with Promises given by other libraries like in fetch
, axios
, or bcrypt
. You can chain any dependent action with .then()
, and catch error with .catch()
// Schedule a Fetch promise
fetch("https://example.com")
.then(value => document.title = value.toUpperCase())
.catch(err => { console.error(err); document.title = "Error" } )
Note that Promises are queued into the background by JavaScript to be checked back later, so the code below will not work like you expected, as you would be mismatching synchronous code and asynchronous code.
let title = undefined
// After promise is resolved, `value` will be assigned to `title`
// But for now, JS will queue this task onto the background, and
// continue immediately with the next line
newValuePromise().then(value => title = value)
// Therefore, even if you console.log here, you'd still get `undefined`,
// no matter how fast or instant your Promise is
// Once you are in Promise land, you can continue the chain, but
// can't go back out to the synchronous world.
console.log(title)
One thing to watch out for is that only the value you return from the previous .then
is available to the next .then
. If you forgot to return any value, the next .then
won't have it.
fetch("https://example.com")
.then(value => {
return value.toUpperCase()
})
.then(upperCaseVal => {
console.log(upperCaseVal)
})
.then(value => {
// Since the previous `.then` only console.log and did not
// return any value, this `.then` does not have any value
console.log(value) // undefined
})
Async and Await
Async/Await is another syntax to work with Promises besides .then()
. If you tag your function with async
, then you use await
instead of .then
to continue the promise chain
async function handleResponse() {
const response = await fetch("https://example.com");
if (response === "happy") {
// If happy response, then add a new diary entry
const anotherResponse = await fetch("http://diary.com/new");
console.log(anotherResponse);
return anotherResponse;
} else {
return "normal day";
}
}
is equivalent to
function handleResponse() {
fetch("https://example.com").then((response) => {
if (response === "happy") {
return fetch("http://diaray.com/new").then((anotherResponse) => {
console.log(anotherResponse);
});
} else {
return "Normal day";
}
});
}
You can see how async/await really simplify the code flow and nesting, and is much more readable.
Note that the async function still returns a Promise. In an async function, it might look like you are writing normal synchronous JavaScript, but async/await is just a syntactic sugar over .then()
, and whatever value you return from an async function will be wrapped into a Promise.
Some random caveats
setTimeOut()
is actually not a Promise
You will notice that setTimeOut
is used a lot in an asynchronous settings, either to stimulate Promise taking time in tutorials, or to schedule something that JS will come back to in the future. However, setTimeOut()
actually does not return a Promise itself, and instead works with callback function (old-school style). You can convert setTimeOut()
to a sleep()
function that returns a Promise like below:
// Making setTimeOut return a resolved Promise after `ms` time
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function test() {
await sleep(1000);
document.title = "Awoken after 1000ms sleep";
}
// Or without async
sleep(1000).then(() => document.title = "Changed")
Top comments (0)