What is a JavaScript promise?
JavaScript Promises let you write code that may eventually resolve to a value that you don't know yet. I say may because a value isn't guaranteed. You might want to throw an error. Keep reading to learn more.
function handleResult(result) {
console.log(result);
}
getResult().then((result) => {
handleResult(result);
});
Here we call a function named getResult
which returns a Promise. To get the result
we call then
on the promise that's returned by getResult
and pass it a callback function. The callback function you pass into the then
function isn't guaranteed to run right away, or ever really. But if the getResult
function has any data that it wants to get back to the caller, it'll be sent in a way that will invoke that callback function.
So what does getResult
look like?
function getResult() {
return new Promise((resolve) => {
resolve("value");
});
}
Here's an example of what getResult
could look like. This isn't a really helpful function though, right? But let's go through what it does anyway.
The getResult
function returns a Promise, we expected that much. The Promise it returns is instantiated with a callback function that accepts a resolve
argument, but why? The resolve
argument is how the Promise can return data to the caller. When you invoke resolve
inside the Promise callback the then
function is invoked above. You now have a very good basic understanding of Promises.
What about errors?
I intentionally left out error handling for simplicity, but let's see how that works with a Promise.
function handleError(error) {
console.error(error);
}
function handleResult(result) {
console.log(result);
}
getResult().then((result) => {
handleResult(result);
}).catch((error) => {
handleError(error);
});
Notice I added a catch
call above and passed it a callback function. Something to note here, we chained the calls, the reason we can do that is because the then
function returns the original Promise that getResult
returned as well. The following code is equivalent, but longer.
const promise = getResult();
promise.then((result) => {
handleResult(result);
});
promise.catch((error) => {
handleError(error);
});
Now the last thing to know is how a Promise invokes the catch
function.
function getResult() {
return new Promise((resolve, reject) => {
reject("something went wrong");
});
}
Bringing it all together
Here's an example of a function that let's you pick if a Promise resolves (calls then
) or is rejected (calls catch
)
function getResult(shouldReject) {
return new Promise((resolve, reject) => {
if(shouldReject) {
reject("was told to reject");
} else {
resolve("resolving...");
}
});
}
// this chain will only ever call `then`
getResult().then((result) => {
console.log(result);
}).catch((error) => {
console.error(error);
});
// this chain will only ever call `catch`
getResult(true).then((result) => {
console.log(result);
}).catch((error) => {
console.error(error);
});
How does this compare with using async/await?
async
/await
and Promise are interchangeable.
function getResult(shouldReject) {
return new Promise((resolve, reject) => {
if(shouldReject) {
reject("was told to reject");
} else {
resolve("resolving...");
}
});
}
// this will never call the catch block
try {
const result = await getResult();
console.log(result);
} catch(error) {
console.error(error);
}
// this will always execute the catch block
try {
const result = await getResult(true);
console.log(result);
} catch(error) {
console.error(error);
}
The code above works with the same getResult
function from earlier. That's because async/await and Promises are compatible. Here's another example, notice the the getResult
function is now async, but the code that uses it is using then/catch.
async function getResult(shouldReject) {
if(shouldReject) {
throw "was told to reject";
} else {
return "resolving...";
}
}
// this chain will only ever call `then`
getResult().then((result) => {
console.log(result);
}).catch((error) => {
console.error(error);
});
// this chain will only ever call `catch`
getResult(true).then((result) => {
console.log(result);
}).catch((error) => {
console.error(error);
});
What does all this have to do with asynchronous code?
All the previous examples resolved or rejected a value instantly. This isn't very useful, you could have just returned the value instead of a Promise. So how can this be used in a more asynchronous way? Here's an example of using XMLHttpRequest
to fetch some data. This example is incomplete, but it shows how you could use a Promise to eventually return a value request.response
or possibly reject the Promise and return an error.
function fetchData(url) {
return new Promise((resolve, reject) => {
var request = new XMLHttpRequest();
request.onreadystatechange = () => {
if (request.readyState === 4) {
resolve(request.response);
}
};
request.addEventListener("error", (e) => {
reject(e);
});
request.open("GET", url);
request.send();
});
}
fetchData("public/test.txt")
.then((data) => console.log(data))
.catch((e) => console.error(e));