Asynchronous Javascript

-

Photo by Silas Baisch on Unsplash

By default, JS executes code line by line in a synchronous way. While doing so, the page is unresponsive. We don't notice it because its usually very fast. Some specific tasks take a long time to execute, but we cannot afford blocking the page. How to solve this?

Start and set aside

The solution is to set aside the long task while it's waiting. It can be waiting for a server response, waiting for a specific amount of time and so on. Later, once the task is done, an event is emitted along with the result. JS catches that event and triggers any handler we provide.

  1. Start the execution of the long task

  2. Tell the task to call us back once its done

  3. Proceed to do other things

  4. Receive a notification from the task saying its done

  5. Run the code handling the result

We don't know in advance when the task will finish. It may resolve quickly or it may never resolve. It can also fail, when the task is rejected. The code that handles the result, be it a success or a failure, is the handler. The handler can be provided in different ways and at different times. You can provide the handler later, even after the task is done. In that last case, the handler would run immediately.

Example

A long task, that needs to wait 10 seconds before sending back a result:

1. function longTask(resolve,reject){ 2. // this task resolves after 10 seconds and sends the value "Paris" 3. setTimeout(()=>resolve("Paris"),10000) 4. }

resolve and reject are provided by JS, and trigger the desirable effects

A handler, that takes a result and display either the data or the error:

1. function handler(city,error){ 2. console.log(city) 3. console.log(error) 4. }

Indicating to JS that longTask() is a blocking function that requires a specific treatment. We use the Promise constructor:

1. .. 2. (new Promise(longTask)).then(handler) 3. ..

At line 2, JS started longTask and proceeded to the next line. Then, once longTask completed, JS received a notification along with the result and ran the handler.

We can do it in 2 steps instead, keeping a reference so that we can assign the handler later on. taskRef can be used to link the long task to the handler:

1. .. 2. const taskRef = new Promise(longTask) 3. taskRef.then(handler) 4. ..

taskRef can be used with await. But the behaviour is different. It blocks the flow and can lead to unresponsive behaviour. It must be used carefully:

1. .. 2. const taskRef = new Promise(longTask) 3. const result = await taskRef 4. ..