Week 5: Async programming

By Celestine Auburger

Peerigon
Peerigon
3 min readNov 28, 2018

--

Celestine is currently taking an apprenticeship at Peerigon to become a professional web developer. As part of her weekly routine, she publishes a blog post each week about what she has been doing and what she has learned.

Problem to solve

JavaScript can only do one task at once. Therefore, code would be blocked from being executed while e.g. waiting for a server to respond. There are different approaches to solve these problems.

Photo by Brad Neathery on Unsplash

Solution

There are four solutions I want to introduce: callbacks, promises, generators and async/await.

Callbacks

They are the easiest way to write async code, but lead to a so called callback hell. Which means the code is hard to read because the functions are nested inside each other. The callback function is usually executed later. It’s a way to communicate back to the caller that something has been finished. An example for a function with a callback and proper error handling is given below. Notice that by convention, the callback should always be the last argument.

function fetchBlogPosts(url, (error, blogPost) => {
if (error) {
alert('Oh, no!');
return;
}
render(blogPost);
});

Promises

Promises are there to save you from callback hell. They turn something like this:

someAsyncFunction((error, result) => {
someAsyncFunction((error, result) => {
someAsyncFunction((error, result) => {
});
});
});

into that:

someAsyncFunction()
.then(someAsyncFunction)
.then(someAsyncFunction)
.then(someAsyncFunction)

Which is way more readable.

Promises are created with an executor function which accepts a resolve and a reject callback: const p1 = new Promise((resolve, reject) => {});. The executor is called synchronously.

Promises are settled in the end, which means they are either fulfilled (success case) or rejected (error case). To handle a settled promise, there are these functions:

  • then(onFulfilled, onRejected): can have two listeners, where onRejected is optional.
  • catch(onRejected): just listens for the error case
  • finally(): is executed when the promise is settled, no matter if fulfilled or rejected.

For further reading, please refer to Promises.

Generators

A generator is a special iterator created with function* generateSomething(){}. This function does not actually execute when it's called, but returns a generator instance which provides the following methods:

  • next(someValue): Passes someValue to the generator and executes the function till the next yield or return. It then returns an object with two properties, done and value, where done marks if the generator is finished.
  • return(): immediately finishes the generator.
  • throw(): throws an exception to the generator.

A generator can be used to create pausable functions. Everytime the generator hits a yield or return, it stops. Later, it can be resumed using the next() method. This makes them suited for asynchronous tasks that might take longer.

Async/Await

This is the new option to write asynchronous code in a synchronous way since ES7. The keyword async in front of a function declaration marks it as asynchronous. Inside this function you can wait for a returned promise by using await. Furthermore, it always returns a promise, even without using await. It works similar like a generator and under the hood, it uses the same foundation: a pausable function.

What I learned

  • There are always multiple solutions for one problem, some are easier than others.

--

--

Peerigon
Peerigon

We build cutting-edge software based on open web standards.