JavaScript Fetch API response to global...

Β·

5 min read

JavaScript Fetch API response to global...

Fetch in a nutshell: 🐢 JavaScript Fetch API is a great way to fetch resources asynchronously across the network. It was introduced in ES6. Fetch API is a Promise-based API used to fetch resources across web servers, almost the same as AJAX. However, unlike AJAX, Fetch provides easy syntax and offers more features. Fetch API also can send or receive data like XML, JSON, TEXT and HTML from web servers without reloading providing a Rich Application Development architecture. Besides, it can fetch from Web API or REST APIs. Fetch API is based on Request and Response. The request is the incoming stream to the server and Response is the outgoing stream from the server.

A simple request

fetch(Request)

A simple response : not so simpleπŸ˜‰

 fetch(req).then(res=>response.json()).then(res=>console.log(res));
// or can be written like 
 fetch(req)
.then(res=>response .json())
.then(res=>console.log(res));

Fetch data and store it to a global variable: A problem? πŸ™‹ πŸ™‹πŸ™‹

Because Fetch does things asynchronously hence the response will be only rendered once available. Now if you want to use Fetch to fetch some server data and store this into an earlier declared variable then you will notice that it will not work, seems like the data wasn't assigned at all. This is because it takes time to perform fetching; Firstly, at the time of calling the console.log the variable is not yet set, secondly, fetch will not block and allow for the following lines to execute without waiting for it. So you search Google for a solution and find lots of ways around it, how to etc. Some may suggest using a bunch of awaits with async, which in turn makes you redesign and remodel your code. Some even start an argument stating "How not to pollute globals", or "Accedental / unplanned changes to global" etc...

There are actually easy ways to achieve this without breaking the principles of software engineering or concepts. I will try to give a simple example of how: Consider the following example. We will use JavaSript and PHP language to demonstrate this.

Let's say, I have a few products coming from a shopping cart database table :

<?php
$db_cart = [
    [
        'product_id' => 3,
        'product_name' => 'Product 3',
        'product_price' => 70.00,
        'product_image' => 'https://placehold.co/450x300?text=image',
        'product_status' => 'Popular Item',
    ],
    [
      'product_id'=> 5,
      'product_name'=> 'Product 5',
      'product_price'=> 30.00,
      'product_image'=> 'https://placehold.co/450x300?text=image',
      'product_status' => 'Sale Product'
    ]
];

if(isset($_GET['action'])){
    $action = $_GET['action'];
    if($action == "get_products"){
        echo json_encode($db_cart);
    }
}

?>

Now let's write our Fetch: 🐢

let products = []; // Global to store products 
// Getting cart(products) from database 
function getProducts(){
  fetch("get_product.php?action=get_products")
  .then((response)=>response.json())
  .then((response)=>{
    console.log("Getting Cart item from Database" )
    products = response; // We assign response to global variable
    console.log(products); // will display products
  })
}

// Now lets print the products: 
// This should print the products but will print an empty array =>[]  !!!
console.log(products) ;

You will notice that the last console.log(products)has been executed before and it was empty, but console.log(products) within the fetch operation will print the product details. This is because 1) Fetch doesn't block, 2). By the time the last console.log() executed maybe Fetch wasn't available with data that time⌚, AKA asynchronous nature. Here is a screen-read.

If you aren't too concerned about "polluting globals or accidental change etc" at this point then there are two ways you can solve this without the use of a bunch of awaits, async etc or refectoring the codebase. Let's have a look at each:

Solution 1 πŸ‘‰: Using a function with a parameter or even some sort of setup() / init() function with a parameter. Let's consider that we will also have a function that will generate product details UI's on the view/page and let's call it generateProducts();

let products = []; // Global to store products 
// ====== Solution 1: ========= ///
// Getting cart(products) from database 
function getProducts(){
  fetch("get_product.php?action=get_products")
  .then((response)=>response.json())
  .then((response)=>{
    console.log("Getting Cart item from Database" )
    console.log(response);
    generateProducts(response) 
  })
};

//Will generate product details UI using this function 
function generateProducts(data){
  products = data;
  console.log(products) ; // you will see the products 
// Continue rendering the product UI here
}

The above example, Solution 1, will adhere to the concept of state transfer through parameters. But if you for some reason don't want to use the parameterised generateProducts() function then you can opt for solution 2.

Solution 2 πŸ‘‰ : Simply assign the response data to the global products variable and call generateProducts() within the fetch operation as shown below:

// ====== Solution 2: ========= ///
let products = []; // Global to store products 
// Getting cart(products) from database 
function getProducts(){
  fetch("get_product.php?action=get_products")
  .then((response)=>response.json())
  .then((response)=>{
    console.log("Getting Cart item from Database" )
    console.log(response);
    products = response ; // Assign response to global
    generateProducts() ; // Initiate the function call 
  })
};

function generateProducts(){
  console.log(products) ;
  // Continue rendering the product UI here
}

Solution 2, In this case, generateProducts() function will get called before the Fetch is done with its operation and as it passes the data before the function call then you will see the product data being printed within generateProducts() this time. Below is a screen output:

Here we can see both sets of data are available. One within the fetch and another within the generateProducts(). πŸ‘‰πŸ‘ΉπŸ‘ˆ

Avoid Global? : However, if you want to avoid global due to scope issues, testability and modularity or say for smaller applications where state management libraries might be also an overkill, neither you want to use parameterised generateProducts() function then define variables within the functions where they're needed as follows :

// Redesign fetch with async & await
const getProductData = async () => {
  const response = await fetch("get_product.php?action=get_products");
  const products = await response.json();
  return products;
};

// Function to render product GUI 
const generateProducts = () =>{
  getProductData().then(products => {
    //Render your GUI here
    console.log(products);
  });
}

// Call generateProducts whenever you want
generateProducts();

This should solve both problems! πŸ‘ πŸ¦‹ πŸ‘

Β