Open In App

How “Control Flow” Controls the Functions Calls ?

Last Updated : 18 Jun, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Control flow is a fundamental concept in programming that determines the order in which code statements and function calls are executed. By controlling the flow of a program, you can create complex logic, handle different execution paths, and manage the sequence of function calls. Understanding control flow is crucial for writing efficient and maintainable code.

This article explores how control flow controls function calls in programming, covering key concepts, common patterns, and practical examples.

What is Control Flow?

Control flow refers to the order in which individual statements, instructions, or function calls are executed or evaluated in a programming language. Control flow mechanisms allow a program to make decisions, repeat operations, and jump to different sections of code based on certain conditions.

Key Control Flow Mechanisms

  • Sequential Execution: Code is executed line by line, from top to bottom.
  • Conditional Statements: if, else if, and else statements allow code to be executed based on specific conditions.
  • Loops: for, while, and do...while loops enable repetitive execution of code blocks.
  • Function Calls: Functions are invoked based on the program’s flow and the logic defined.
  • Exception Handling: try, catch, and finally blocks manage errors and exceptions in the program.

Controlling Function Calls with Control Flow

Function calls are integral to the control flow in programming. They enable modular and reusable code. By controlling when and how functions are called, you can manage the complexity of your program.

Sequential Function Calls

In sequential execution, functions are called one after another in the order they are written.

JavaScript
function greet() {
    console.log('Hello');
}

function farewell() {
    console.log('Goodbye');
}

greet();
farewell();

Output
Hello
Goodbye

Note: In this example, greet() is called first, followed by farewell(), resulting in the output

Conditional Function Calls

Functions can be called conditionally based on the outcome of conditional statements.

JavaScript
function showSuccessMessage() {
    console.log('Operation successful');
}

function showErrorMessage() {
    console.log('Operation failed');
}

const operationSuccess = true;

if (operationSuccess) {
    showSuccessMessage();
} else {
    showErrorMessage();
}

Output
Operation successful

Note: If operationSuccess is true, showSuccessMessage() is called. Otherwise, showErrorMessage() is called.

Control flow in Node.js is typically managed using one of three methods: callbacks, promises, and async/await.

Callbacks

Callbacks are functions that are passed as arguments to other functions and are executed when that function completes its task. In Node.js, many functions are asynchronous, and they often take a callback as an argument. When the asynchronous operation is completed, the callback is executed, allowing the program to continue its execution.

Steps to use a callback

  • Define the function that accepts a callback parameter. This function should perform some asynchronous operation and then call the callback with the result.
  • When calling the function, pass a callback function as an argument. This callback will be executed once the asynchronous operation is complete.
  • Within the callback function, you can handle the result of the asynchronous operation.

Syntax:

function functionName(param1, param2, callback){
// Asynchronous operation
callback(result);
}

Example 1: Implementation to show the use of callback with an example.

JavaScript
const multiply = (a, b, callback) => {

    // Multiply the values of a and b
    const result = a * b;

    // Pass the result to the callback function
    callback(result); 
}

multiply(2, 3, (result) => {

    // Log "Callback" to the console
    console.log("Callback"); 

    // Log the result with a prefix "Ans: "
    console.log("Ans: " + result); 
});

Output
Callback
Ans: 6

In the above example, the multiply function takes two numbers as arguments and a callback function that is executed when the multiplication is complete. The callback function simply logs the result to the console. The arrow function syntax is used to define the multiply function and the callback function.

Example 2: Implementation to show the use of callback with an example.

JavaScript
// Function that performs an asynchronous operation
// and invokes a callback when done
const fetchData = (url, callback) => {

    // Simulating an asynchronous operation 
    // (e.g., making an API request)
    setTimeout(() => {
        const data = { id: 74, name: "Geeks for Geeks" };
        callback(data);
    }, 2000); // Simulating a 2-second delay
}

// Callback function that handles the fetched data
const handleData = (data) => {
    console.log("Fetched data:", data);
}

// Call the fetchData function with the
// URL and the callback function
fetchData("https://example.com/api/data", handleData);

Output
Fetched data: { id: 74, name: 'Geeks for Geeks' }

Explanation:

In the above example, the fetchData function simulates an asynchronous operation (like making an API request) by using setTimeout. After the simulated delay, it invokes the callback function callback with the fetched data. The callback function handleData receives the fetched data and performs some operations with it. In this case, it logs the data to the console. When we call the fetchData function, we pass the URL, and the callback function handleData.

Promises

Promises in JavaScript are used to manage asynchronous operations more effectively compared to traditional callbacks. They represent a value that will be available in the future. When the value is ready, the promise is either fulfilled or rejected, triggering the corresponding code execution. This helps avoid “callback hell” and makes code more readable and maintainable. Promises are created using the `Promise` constructor, which accepts a function defining the operation. Once the operation completes, the promise is either resolved or rejected. Promises support chaining with the `.then()` method, facilitating the creation of complex workflows.

Steps to define a promise:

  • Create a Promise using the Promise constructor, which takes a function with resolve and rejects parameters.
  • Use the resolve function to fulfill the promise with a value or the reject function to reject the promise with an error.
  • Handle the fulfilled and rejected states of the promise using the .then() and .catch() methods.

Syntax:

function functionName(){
return new Promise(function(resolve, reject){
// Operation
});
}

functionName()
.then(result)
.catch(error);

Example 1: Implementation to show the use of promises with an example.

JavaScript
// Function that divides two numbers
// and returns a promise
function divide(a, b) {
    return new Promise(function (resolve, reject) {
        if (b === 0) {

            // Reject the promise with an
            // error if b is zero
            reject(new Error("Cannot divide by zero"));
        } else {
            // Resolve the promise with 
            // the result of the division
            resolve(a / b);
        }
    });
}

// Call the divide function with arguments 10 and 2
divide(10, 2)
    .then(function (result) {

        // Log "Promise" to the console
        console.log("Promise"); 

        // Log the result with a prefix "Ans: "
        console.log("Ans: " + result); 
    })
    .catch(function (error) {

        // Log any errors that occurred
        // during the division
        console.log(error); 
    });

Output
Promise
Ans: 5

Exaplanation:

In the above example, the divide function returns a promise that resolves to the result of dividing a by b. If b is zero, the promise is rejected with an error message. Then the promise is used in the then method to handle the fulfillment of the promise (i.e. when the result is available) and the catch method is used to handle the rejection of the promise (i.e. when an error occurs). In this case, the result of the division is logged into the console.

Example 2: Implementation to show the use of promises with an example.

JavaScript
// Function that performs an asynchronous
// operation and returns a promise
function getUser(id) {
    return new Promise(function (resolve, reject) {

        // Simulating an asynchronous 
        // operation (e.g., fetching
        // user data from a database)
        setTimeout(() => {
            const users = {
                1: { id: 73, name: "Geek" },
                2: { id: 74, name: "Geeks for Geeks" },
                3: { id: 75, name: "M." }
            };

            if (users[id]) {

                // Resolve the promise with
                // the user data if found
                resolve(users[id]);
            } else {

                // Reject the promise with an
                // error if user not found
                reject(new Error("User not found"));
            }
        }, 2000); // Simulating a 2-second delay
    });
}

// Call the getUser function with user ID 2 and 
//handle the promise using then and catch
getUser(2)
    .then(function (user) {

        // Log the retrieved user data to
        // the console
        console.log("User:", user);
    })
    .catch(function (error) {

        // Log any errors that occurred during
        // the asynchronous operation
        console.log("Error:", error);
    });

Output
User: { id: 74, name: 'Geeks for Geeks' }

Explanation:

  • In the above example, the getUser function takes a user ID as an argument and returns a promise. Inside the promise, we simulate an asynchronous operation by using setTimeout. After the simulated delay, we check if the user with the given ID exists in our sample user data.
  • If the user is found, we resolve the promise with the user data. If the user is not found, we reject the promise with an error.
  • We call the getUser function with user ID 2 and handle the promise using the then and catch methods. The then method is used to handle the resolved promise and receive the user data. The catch method is used to handle any errors that occurred during the asynchronous operation.
  • When the promise is resolved, we log the retrieved user data to the console. If there are any errors, we log the error message instead.

Async/await

async/await in Node.js simplifies asynchronous code to look and behave more like synchronous code, enhancing readability and maintainability. Marking a function with async makes it return a promise, while await pauses execution within the async function until the awaited promise resolves or rejects, returning its value. This approach avoids the complexity of nested callbacks and makes asynchronous code easier to manage compared to traditional callbacks or promises.

Some steps to use an Async/await

  • Define an asynchronous function using the async keyword before the function declaration.
  • Within the asynchronous function, use the await keyword to pause the function until a promise is fulfilled or rejected.
  • When using await, make sure the expression following it is a promise. If it is not a promise, it will be implicitly converted to a resolved promise with the value of the expression.
  • Use try-catch block to handle any errors that may be thrown by the awaited promise.
  • Call the asynchronous function and handle the resolved or rejected promise using .then() and .catch() as you would with regular promises.

Syntax:

async function functionName(){
await wait(ms);
}

functionName().catch(() => {});

Example 1: Implementation to show the use of async await with an example.

JavaScript
// Function that returns a promise that
// resolves after a specified time
function wait(time) {
    return new Promise(
        resolve => setTimeout(resolve, time));
}

// Async function that demonstrates
// the usage of async/await
async function example() {

    // Log a message indicating the 
    // start of the async function
    console.log("Async/await Starting...");

    // Wait for 1 second
    await wait(1000); 

    // Log a message after 1 second has passed
    console.log("One second has passed!");

    // Wait for 2 seconds
    await wait(2000); 

    // Log a message after 2 more
    // seconds have passed
    console.log("Two more seconds have passed!");
}

// Call the async function to start
// the execution
example(); 

Output
Async/await Starting...
One second has passed!
Two more seconds have passed!

Explanation:

In the above example, we define a wait function that returns a promise that resolves after a given amount of time (in milliseconds). We then define an example async function that uses await to pause execution until the promises returned by the wait function have resolved. When the example is called, it logs a message, waits for one second, logs another message, waits for two more seconds, and logs a final message. Because we are using async and await, we don’t have to use callbacks or chain promises together, which can make our code more readable and easier to reason about.

Example 2: Implementation to show the use of async await with an example.

JavaScript
// Async function that adds two
// numbers and returns the sum
async function add(a, b) {

    // Add the numbers a and b
    const sum = a + b; 
    return sum; // Return the sum
}

// Async function that demonstrates
// the usage of async/await
async function run() {

    // Await the result of adding 2 and 3
    const result = await add(2, 3);

    // Log a message indicating the 
    // use of async/await
    console.log("Async/await");

    // Log the sum with a prefix "Sum is: "
    console.log("Sum is: " + result);
}

// Call the async function to
// start the execution
run(); 

Output
Async/await
Sum is: 5

Explanation:

In the above example, the add function is marked as async, indicating that it returns a promise. The run function is also marked as async, and it uses the await keyword to wait for the promise returned by add to resolve before continuing execution.

Overall, in Node.js, the control flow of function calls can be managed using callbacks, promises, or async/await, depending on the specific needs and requirements of the program. These mechanisms provide a powerful and flexible way of managing the flow of function calls, allowing Node.js developers to build highly efficient and scalable applications.



Next Article

Similar Reads