UNDERSTANDING CALLBACKS, PROMISES, GENERATORS AND ASYNC/AWAIT

There are few fields in JavaScript that provide an equal amount of possible solutions and tools to the handling of asynchronous code. There is a reason for the existence of the vast amount of tools. Handling asynchronous code in a readable and managea…


This content originally appeared on DEV Community and was authored by Muhammad Muhktar Musa

There are few fields in JavaScript that provide an equal amount of possible solutions and tools to the handling of asynchronous code. There is a reason for the existence of the vast amount of tools. Handling asynchronous code in a readable and manageable way has always been challenging in JavaScript. We are going to try to understand callbacks, promises, generators and async await so as to know when to use each tool and how they actually differ.
By default JavaScript is a synchronous language. This means it executes one line of code at a time and hence the need for certain approaches and tools to handle asynchronous code. Let us look at this approaches.

CALLBACKS

Callbacks are the oldest and simplest way to deal with asynchronous code in JavaScript. Callbacks are also known as higher order functions. They functions passed into functions and they are executed at some point. let us have a look at an example code

let x = function () {
    console.log("i am called from inside a function");
};

let y = function (callback) {
    console.log("do something here");
    callback();
};

y(x);

In the above example we have a function x and another function y. Function y has an argument called callback and it is being executed as a function callback() within the function y as shown in the image below

image

When function y is executed it passes function x as an argument and it will execute inside function y as an argument and a callback.

image

Note that we are passing function x into function y as a function body. We are not passing the result of function x. We are passing the function body itself into another function and it would be executed at some point. Let us run the code and see what happens

image

When we run the code as in above, the first line of code in function y

 console.log("do something here");

gets executed and the console prints the result. After this the function x gets executed printing to the console too. This is asynchronous behavior and a very simple way of executing callbacks.
The problem with callbacks is that when the application gets bigger, we have to do a lot of nesting which can lead to what is called callback hell . Error handling becomes difficult in this situation. Hence JavaScript gave us a solution called promises introduced in ES6

PROMISE

A promise in JavaScript is like a promise in real life. The promise has two outcomes, which are either the promise is resolved or the promise fails. Let us look at the syntax for creating a promise
We create a variable and set it to a new promise

let p = new Promise

This promise is an object and it takes a parameter which is a function. This function takes two parameters which are a resolve and a reject.

let p = new Promise((resolve, reject) => {

});

Then we need to create a definition of the function. We need to define what the actual promise is

let p = new Promise((resolve, reject) => {
    let x = 2 + 2;
});

let x = 2 + 2; is what the promise does. If it resolves to true, we resolve the promise

let p = new Promise((resolve, reject) => {
    let x = 2 + 2;
    if (x == 4) {
      resolve ('done')  // we can pass in anything we want
    }
});

if the promise does not resolve the promise rejects

let p = new Promise((resolve, reject) => {
    let x = 2 + 2;
    if (x == 4) {
        resolve('done')
    } else {
        reject('error'); //we can also pass anything we want
    }
});

The above code is always going to resolve because 2 + 2 = 4 is always going to resolve. If we change the code to be 2 + 3 which will give us 5, the code is going to reject because then x will not be equal to 4. let us now look at how we interact with a promise. Below the code block we can now say

p.then()

This is our promise and everything inside the

.then()

is going to run for a resolve. The

.then()

is going to take a single parameter and in our case it is going to be

p.then(message => {

});

We want to decide what we want to do with our message, thus we can pass a message to resolve the promise.

p.then(message => {
console.log("this is a message" + message);
});

To catch an error in a promise, we need to add the

.catch()

method to the promise. It will catch any error in our promise.

p.then(message => {
console.log("this is a message" + message);
}).catch(error=>console.log('error'));

The promise is fulfilled from the above code.

image

This is exactly how a promise is used. They are very similar to callbacks but they are a little bit cleaner way of doing callbacks.
Promises are really great when something is to be done in the background. The error can also be caught if it fails and a message can be sent if it fails.

GENERATORS

A generator is a function that can be paused. This will allow the writing of code in an asynchronous fashion. Let us go straight to the syntax. After defining a function, an asteryx is added to the function keyword

function* car() {

}

values can now be yielded and stored into a variable

function* car() {
const variable = yield value;
};

Essentially, once that value is resolved or returned from whatever computations performed, it will be stored in a variable. The yield keyword can be used multiple times.

function* car() {
const numb2 = yield 2;
const numb3 = yield 3;
const numb4 = yield 4;
const numb5 = yield 5;
};

and so on. After defining the generator, it needs to be setup to be actually used. We do this by setting the function to a variable

const gen = car();

The function has been set and it is ready to get all values from the generator. To get the value from the generator we can use a series of methods like

gen.next()
gen.next().value
gen.next().done
next()

is an object. The object contains a property called values which represents whatever value that is yielded from the generator. The

next().done

is a Boolean that represents whether the generator has simply finished. Let us take an example in code

const getNumber = function* () {
    yield 2;
    yield "hello";
    yield true;
    yield { name: "anna" }
};

To use the above function we have created as a generator, assign it to a variable

const numberGen = getNumber();

If the code is is executed, nothing really will happen because we invoked the function

getNumber()

without traversing it line by line.

const numberGen = getNumber();
console.log(numberGen.next());

If the above code is executed, we will get an object.

image

The object shows that line one of the generator and that

next().done

is false. To get the whole value, we can duplicate the

next()

function a couple of times

const getNumber = function* () {
    yield 2;
    yield "hello";
    yield true;
    yield { name: "anna" }
};

const numberGen = getNumber();
console.log(numberGen.next());
console.log(numberGen.next());
console.log(numberGen.next());
console.log(numberGen.next());

image

You should see that we have a few objects and at the bottom we get a value done: true. which means that our generator has finished traversing the function.
To get the actual value of our generator, we simply append

.value;

to

.next()

method

console.log(numberGen.next().value);

image

and we get our values. To generate a value when done, simply add a return statement at the end of the function

const getNumber = function* () {
    yield 2;
    yield "hello";
    yield true;
    yield { name: "anna" };
    return 'i am done'
};

const numberGen = getNumber();
console.log(numberGen.next().value);
console.log(numberGen.next().value);
console.log(numberGen.next().value);
console.log(numberGen.next().value);
console.log(numberGen.next().value);

image

Promises can be used along with generators. It is an interesting feature. We will leave that for another day

ASYNC/AWAIT

Async/await is JavaScript baking callbacks, promises and generators into a single function. Let us take a look at the async/await syntax

async function logName(name) {
    console.log(name);
}
logName('Anna');

image

we get a name 'Anna'. now remove the async keyword from the code

 function logName(name) {
    console.log(name);
}
logName('Anna');

We still get the same exact response. We can see that when we have the async keyword appended to the function, we can yield promises inside the function body using the await keyword.
The second thing is that the function returns a promise. For example

async function logName(name) {
    console.log(name);
}
logName('Anna').then(res=> {
    console.log('hello from me' + res);
});

image

As you can see we get a response which is a promise. If we remove the async keyword from the function, we most likely will get an error.

image

Using the async keyword let us go inside the function and create a new promise


async function logName(name) {
    console.log(name);
    const tranformName = new Promise(function (resolve, reject) {
        setTimeout(() => {
            resolve(name.toUpperCase())
        }, 2000);
        return name
    });
};

Now we can go ahead and use the promise. The way that we use the promise is we can use the keyword called await.

async function logName(name) {
    //we can yield promises using await
    const transformName = new Promise((resolve, reject) => {
        setTimeout(() => resolve(name.toUpperCase()), 2000);
    });
    const result = await transformName;
    console.log(result);
    //it returns a promise
    console.log(name);
    return result;
};

This will return the actual result after the setTimeout method runs.

image

So basically those are the two things you need to know when using the async/await. Whatever is returned from the function ends up being a promise.


This content originally appeared on DEV Community and was authored by Muhammad Muhktar Musa


Print Share Comment Cite Upload Translate Updates
APA

Muhammad Muhktar Musa | Sciencx (2021-07-13T00:05:45+00:00) UNDERSTANDING CALLBACKS, PROMISES, GENERATORS AND ASYNC/AWAIT. Retrieved from https://www.scien.cx/2021/07/13/understanding-callbacks-promises-generators-and-async-await/

MLA
" » UNDERSTANDING CALLBACKS, PROMISES, GENERATORS AND ASYNC/AWAIT." Muhammad Muhktar Musa | Sciencx - Tuesday July 13, 2021, https://www.scien.cx/2021/07/13/understanding-callbacks-promises-generators-and-async-await/
HARVARD
Muhammad Muhktar Musa | Sciencx Tuesday July 13, 2021 » UNDERSTANDING CALLBACKS, PROMISES, GENERATORS AND ASYNC/AWAIT., viewed ,<https://www.scien.cx/2021/07/13/understanding-callbacks-promises-generators-and-async-await/>
VANCOUVER
Muhammad Muhktar Musa | Sciencx - » UNDERSTANDING CALLBACKS, PROMISES, GENERATORS AND ASYNC/AWAIT. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/07/13/understanding-callbacks-promises-generators-and-async-await/
CHICAGO
" » UNDERSTANDING CALLBACKS, PROMISES, GENERATORS AND ASYNC/AWAIT." Muhammad Muhktar Musa | Sciencx - Accessed . https://www.scien.cx/2021/07/13/understanding-callbacks-promises-generators-and-async-await/
IEEE
" » UNDERSTANDING CALLBACKS, PROMISES, GENERATORS AND ASYNC/AWAIT." Muhammad Muhktar Musa | Sciencx [Online]. Available: https://www.scien.cx/2021/07/13/understanding-callbacks-promises-generators-and-async-await/. [Accessed: ]
rf:citation
» UNDERSTANDING CALLBACKS, PROMISES, GENERATORS AND ASYNC/AWAIT | Muhammad Muhktar Musa | Sciencx | https://www.scien.cx/2021/07/13/understanding-callbacks-promises-generators-and-async-await/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.