There are 2 primary ways of dealing with asynchronous behavior in JavaScript: callback functions and promises.
Callbacks
Timeouts
setTimeout(() => {
console.log("Hi!");
}, 1000);
Geolocation
Run the following in the Chrome console. If you're on a Mac, you might get a warning saying that you need to enable Location Services. Do so, and try again.
const successHandler = (position) => {
console.log(position.coords.latitude, position.coords.longitude);
};
const errorHandler = (error) => {
console.error(error);
};
const options = {};
navigator.geolocation.getCurrentPosition(successHandler, errorHandler, options);
Promises
Suppose I make a promise to wash the dishes today. At this moment, I'm not able to wash them.
My promise can be in the following 3 states:
- Pending: I haven't washed the dishes yet, but the day isn't over!
- Fulfilled: I successfully washed the dishes today.
- Rejected: I failed in fulfilling my promise and didn't wash the dishes today.
In more technical terms:
AJAX Requests with jQuery
const promise = $.ajax({ type: "GET", url: "/api/emails" });
// pending
promise.then(
(emails) => {
// fulfilled
},
(error) => {
// rejected
// this will happen for responses with an HTTP status code
// in the 400 - 599 range
}
);
The above can also be written like:
$.ajax({ type: "GET", url: "/api/emails" }).then(
(emails) => {
// fulfilled
},
(error) => {
// rejected
}
);
// pending
jQuery also offers $.getJSON
as a shorthand:
const promise = $.getJSON("/api/emails");
promise.then(
(emails) => {
// fulfilled
},
(error) => {
// rejected
}
);
Creating a Fulfilled Promise
const promise = Promise.resolve(5);
promise.then((value) => {
console.log(value); // 5
});
Creating a Rejected Promise
const promise = Promise.reject("Uh oh!");
promise.then(
() => {
// does not execute
},
(error) => {
console.error(error); // Uh oh!
}
);
A Promise Chain
Promise.resolve(1)
.then((value) => {
console.log(value); // 1
return 2;
})
.then((value) => {
console.log(value); // 2
return 3;
})
.then((value) => {
console.log(value); // 3
});
A Promise Chain That Returns a Fulfilled Promise
Promise.resolve(1)
.then((value) => {
console.log(value); // 1
return Promise.resolve(2);
})
.then((value) => {
console.log(value); // 2
});
A Promise Chain That Returns a Rejected Promise
Promise.resolve(1)
.then((value) => {
console.log(value); // 1
return Promise.reject("Uh oh!");
})
.then(
(value) => {
// doesn't execute
},
(error) => {
console.error(error); // Uh oh!
}
);
The above can also be written using catch
:
Promise.resolve(1)
.then((value) => {
console.log(value); // 1
return Promise.reject("Uh oh!");
})
.catch((error) => {
console.error(error); // Uh oh!
});
A Promise Chain That Throws An Error
Promise.resolve(1)
.then((value) => {
console.log(value); // 1
throw new Error("Uh oh!");
})
.then(
(value) => {
// doesn't execute
},
(error) => {
console.error(error); // Uh oh!
}
);
The above can also be written using catch
:
Promise.resolve(1)
.then((value) => {
console.log(value); // 1
throw new Error("Uh oh!");
})
.catch((error) => {
console.error(error); // Uh oh!
});
Recovering From an Error
Promise.resolve(1)
.then((value) => {
console.log(value); // 1
throw new Error("Uh oh!");
})
.catch((error) => {
console.error(error); // Uh oh!
return "Everything will be ok";
})
.then((value) => {
console.log(value); // Everything will be ok
});
Creating a Promise with the Promise
class
const promise = new Promise((resolve, reject) => {
resolve(5);
});
promise.then((value) => {
console.log(value); // 5
});
const promise = new Promise((resolve, reject) => {
reject("Uh oh!");
});
promise.then(
() => {
// does not execute
},
(error) => {
console.error(error); // Uh oh!
}
);
Exercise
Create a function called timeout
that behaves like setTimeout
but instead returns a promise that fulfills after the duration in milliseconds has passed. For example:
timeout(2000).then(() => {
console.log("2 seconds have passed!");
});
Handling Multiple Promises
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
Promise.all([promise1, promise2]).then((array) => {
console.log(array); // [1, 2]
});
const promise1 = Promise.resolve(1);
const promise2 = Promise.reject("Uh oh!");
Promise.all([promise1, promise2]).then(
() => {
// does not execute
},
(reason) => {
console.log(reason); // Uh oh!
}
);
AJAX Requests with fetch
fetch("https://pokeapi.co/api/v2/pokemon?limit=10")
.then((response) => {
// this will get called for responses regardless of the HTTP status code
console.log("HTTP Status Code", response.status);
return response.json();
})
.then(
(data) => {
console.log(data);
},
(error) => {
// this will get called if the response
// can't be parsed as JSON
}
);