JavaScript implementing failover logic in fetch()

I am trying to fetch data from an array of URLs using the fetch() request method:

const urls = [
  'https://dewnuf111111.com/configuration',
  'https://dewnuf222222.com/configuration',
  'https://bcsmania.co.uk/test.json'
];

In case there is an error during the fetch (such as site not found or internal server error), I want the script to try the next URL in the array. To achieve this, I have included a counter variable. Once it successfully fetches data from a working URL, it should log 'DONE' to the console. However, my current implementation seems to have some issues.

This is the code snippet I have been working on:

const urls = [
  'https://dewnuf111111.com/configuration',
  'https://dewnuf222222.com/configuration',
  'https://bcsmania.co.uk/test.json'
];

let obj = {a: 'test'};
let counter = 0;

function ajax(url) {
  // Check for last URL in the array
  if (counter < urls.length) {
    return fetch(url)
      .then(response => {
        // Merge response with object
        obj = Object.assign(obj, response.json());
        console.log(urls[counter], obj);
        return Promise.resolve(obj);
      }).catch(error => {
        counter++;
        // Try fetching the next URL
        ajax(urls[counter]);
      });
  }
}

function getConfigurations() {
  return ajax(urls[counter]);
}

getConfigurations().then((configurations) => {
  console.log('DONE', configurations);
});

You can view a preview of the script on JSFiddle.

I'm seeking guidance on where I might be making mistakes. Should I consider making the function async and using await for better results?

Answer №1

Make sure to always return the Promise in your catch block so that you can continue chaining:

.catch(error => {
    attempts++;
    // Make another request
    return fetch(urls[attempts]);
  });

Answer №2

You seem to be combining asynchronous and synchronous code here, which can lead to issues. Your loop is synchronous and doesn't wait for the results before moving on to the next iteration. A simple solution would be to use async-await within the loop to ensure proper handling of the URLs. I've provided a sample below for reference.

Check out this JSFiddle example

const urls = [
  "https://dewnuf111111.com/configuration",
  "https://dewnuf222222.com/configuration",
  "https://bcsmania.co.uk/test.json"
];

let obj = { a: "test" };

async function getConfigurations(urls) {
  let result = null;
  if (counter < urls.length) {
    for (let count = 0, len = urls.length; count < len; count++) {
      try {
        result = await fetch(urls[count]);
      } catch (e) {}
      if (result) break;
    }
  }
  return result;
}

getConfigurations(urls).then(configurations => {
  console.log("DONE", configurations);
});

Answer №3

One way to optimize your code is by separating the process of fetching a list of URLs from how it is handled, like in the Object.assign call you are using.

In this updated version, the function fetchFirstWorking now takes a list of URLs and returns a Promise that resolves with the data fetched from the first active URL. It utilizes recursion instead of a counter for minimal state management.

The core logic is contained within the getConfiguration function.

const fetchFirstWorking = ([url, ...urls], conf) => url
  ? fetch(url, conf)
      .catch( _ => fetchFirstWorking(urls) )
  : Promise.reject('no urls could be loaded')

const getConfigurations = (urls) => 
  fetchFirstWorking(urls)
    .then(res => res.json())
    .then(res => Object.assign({a: 'test'}, res))

const urls = [
  'https://dewnuf111111.com/configuration',
  'https://dewnuf222222.com/configuration',
  'https://bcsmania.co.uk/test.json'
]

getConfigurations(urls)
  .then(console.log)
  .catch(console.warn)

If you replace the last URL with another dummy one, you will notice the console warning being generated:

const fetchFirstWorking = ([url, ...urls], conf) => url
  ? fetch(url, conf)
      .catch( _ => fetchFirstWorking(urls))
  : Promise.reject('no urls could be loaded')

const getConfigurations = (urls) => 
  fetchFirstWorking(urls)
    .then(res => res.json())
    .then(res => Object.assign({a: 'test'}, res))

const urls = [
  'https://dewnuf111111.com/configuration',
  'https://dewnuf222222.com/configuration',
  'https://dewnuf333333.com/configuration',
]

getConfigurations(urls)
  .then(console.log)
  .catch(console.warn)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Adding Floating Point Numbers in TypeScript Objects

Recently, I've been encountering some strange results while trying to sum up my floating point values. The output seems off with 8 decimal places and other unexpected outcomes. All I really want to do is add up the protein values of various objects to ...

Using a Default Value in a Destructured Function Parameter Results in a ReferenceError

When working on setting a default value for the db in my CRUD functions for testing purposes, I encountered a peculiar issue that has left me puzzled. Here's the snippet of code that caused the trouble: import { db } from './firebase' func ...

Discovering the row that was clicked: A step-by-step guide

Hello, I have created a table using JavaScript and now I am looking to determine the row and column that the user has clicked on. Below is the function I am using to generate the table: function doNextSteps() { removeAustriaFromCountries(); //i ...

PHP Array Output: Understanding the Basics

I am relatively new to PHP and faced with a challenge. I have an array $companyStates = array("AR","TX","LA","OK","GA","NC","SC"); It contains a list of US states. My goal is to do two simple tasks with this array: 1) print the values in this format: ART ...

Encountering challenges when adjusting height based on screen size in Angular 6

Utilizing HostListener to adjust the height based on screen size works well. However, during page load, "event.target.innerHeight" returns undefined until the browser height is changed. To address this issue, the value needs to be initialized. Initially, i ...

AngularJS having trouble navigating to a route with a hash from the login page

I am currently navigating through AngularJS for the first time and I have a navigation bar that allows me to either go to the login page or click on home to return to my homepage. On my homepage, there is a list of products and in the same navigation bar, ...

Dynamically using Jquery to input a Maximum Number not functioning as expected

After creating a basic Input and attempting to set the Max value using Jquery, the initial value was set to 77 and then decreased to 50. The example below demonstrates that it works! $(document).ready(function () { minMaxAge("#test-input",0, ...

Error: The getter callback for the component `RNCSafeAreaProvider` must be a function, but it is currently undefined

After attempting to update my React Native app's dependencies using npm update, chaos ensued. To rectify the situation, I reverted back to the previous package-lock.json, deleted the node_modules folder, and re-ran npm i. Surprisingly, instead of res ...

Create a custom chrome browser extension designed specifically for sharing posts on

I'm working on creating a basic chrome extension that features an icon. When the icon is clicked, I want the official Twitter window to pop up (similar to what you see here). One common issue with existing extensions is that the Twitter window remains ...

Having trouble with running sudo commands on Hyper in Windows 10?

Working on a Windows 10 system without any VM software, I've installed hyper to utilize node and npm. My laptop has just one account, which is also the local account administrator. Surprisingly, even though I have all the permissions, I am unable to r ...

Using HTML5 to overlay text on an image with a hyperlink

I'm interested in adding a hyperlink on top of an image using HTML5. Can you provide some guidance on how to achieve this? Regards, Ravi ...

Create and transmit an array of JSON objects

I need help with defining and sending a JSON object array. While I've managed to define a single JSON object, convert it into a string, and send it, I'm stuck on how to do the same for an array of objects. It seems like there might be a simple so ...

Retrieve the order in which the class names are displayed within the user interface

In the following code snippet, each div element is assigned a common class name. <div id="category_9" class="list_item" data-item_ids="[38]"</div> <div id="category_2" class="list_item" data-ite ...

Please input numbers only into the designated field

I am trying to create an input field in my form that only accepts numbers when pasted into it. For example: If I have some random characters like W123W000 on my clipboard, the input field should only paste 123000. Note: This solution is currently only fu ...

What is the best way to retrieve localstorage information within the getStaticProps function in next.js?

Having trouble retrieving local storage data with the following code. localData = { id: localStorage.getItem("id"), token: localStorage.getItem("token"), }; This snippet is housed within a function called getStaticProps ...

Challenges encountered when trying to save a json document

I have a function that successfully connects to an API and retrieves particle, name, and community_price data for various items. Now, I want to save this data into a JSON file with the following structure: [ { "particle": "xxx", "name" ...

Implementing AOP in NodeJS allows for executing a standardized process for handling database exceptions

Currently, I am in the process of developing a nodejs application that interacts with a database using Sequelize. One of my main requirements is to be able to receive an alert (in the form of an SNMP trap) whenever there are connection issues with the data ...

The art of toggling input values with Angular JS

I have a simple application where users input string values into two fields and the text is incorporated into a sentence. I am looking to add a button that, when clicked, will switch the values in the fields. HTML <body> <div ng-app="myApp" ng ...

Introducing the new and improved SweetAlert 2.0, the ultimate solution for

I am currently utilizing SweetAlert2.0, known as "This One" <script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script> It is functioning properly; however, I would like to display a table, div, or HTML element within this swe ...

The fade effect on an element cannot be achieved using setTimeout

I'm trying to call a tooltip and hide it after 9 seconds, but for some reason, it's not working as expected. The issue I'm facing is that the "tooltip" only hides after it initially loads. It doesn't respond when I click on the "Touch ...