The beauty of asynchronous GET requests in VueJS

As a newcomer to VueJS, I am exploring how to make a GET request to the GitHub API. Initially, I made a request to sort users by follower count, resulting in an array ordered in descending order of user logins. Following that, I sent another GET request to each login value to retrieve more detailed data such as avatar URL and repository count. However, I encountered a problem due to issues with asynchronicity - some requests completed faster than others, causing my new array of objects to be out of sequence based on followers count. I attempted to use the sort() method, but struggled to determine the execution order of each function in the JavaScript flow. Can anyone offer assistance?

Below are the components I'm using:

Home.vue

<template>
  <div>
    <div class="header">
      <h1>Devfinder</h1>
      <p>Find relevant developers from Github</p>
    </div>
    <div class="main">
      <SearchBars :fetchLogins="fetchLogins" />
      <CardList :cards="cards" />
    </div>
  </div>
</template>

<script>
import CardList from "./CardList";
import SearchBars from "./SearchBars";
import axios from "axios";

export default {
  name: "Home",
  data() {
    return {
      loginList: [],
      cardList: [],
      cards: [],
      page: 1,
    };
  },
  components: {
    SearchBars,
    CardList,
  },
  methods: {
    fetchLogins(language, location) {
      axios
        .get(
          `https://api.github.com/search/users?q=location:${location}+language:${language}&sort=followers&order=desc&page=${this.page}&per_page=8`,
          {
            headers: {
              Authorization: "***",
            },
          }
        )
        .then((res) => {
          console.log(res.data);
          this.loginList = res.data.items.map((item) => item.login);
          console.log(this.loginList);
        })
        .catch((err) => console.log(err))
        .then(() => this.setCardInfo())
        .then(() => this.cards = this.cardList.sort((a,b) => b.followers - a.followers));
    },
    setCardInfo() {
      let newCardsArray = [];
      this.cards = [];
      this.cardList = [];
      // Reset the state of cards and iterate through the loginList array to send a GET request and create objects for the cards array
      this.loginList.forEach((login) =>
        axios
          .get(`https://api.github.com/users/${login}`, {
            headers: {
              Authorization: "***",
            },
          })
          .then((res) => {
            const user = res.data;
            const cardObject = {
              id: user.id,
              name: user.name,
              avatar: user.avatar_url,
              bio: user.bio,
              followers: user.followers,
              repositories: user.public_repos,
            };
            newCardsArray.push(cardObject);
          })
      );
      // Due to asynchronicity, some objects, even with more followers, end up out of order in the array
      // Invoke a sort() in descending order
      this.cardList = newCardsArray;
    },
  },
};
</script>

and the SearchBars component


<template>
  <div>
    <form @submit="onSubmit">
      <p>Tech/Lang</p>
      <input type="text" v-model="language" placeholder="Type a technology or language" />
      <p>Location</p>
      <input type="text" v-model="location" placeholder="Type the desired location" />
      <input type="submit" value="FIND" />
    </form>
  </div>
</template>

<script>
export default {
  name: "SearchBars",
  data() {
      return {
          language: '',
          location: ''
      }
  },
  methods: {
      onSubmit(e){
          e.preventDefault();
          this.fetchLogins(this.language, this.location);
      }
  },
  props:["fetchLogins", "getCardInfo"]
};
</script>

Answer №1

The issue at hand is that your functions are not asynchronous. While this doesn't pose a problem for fetchLogins, since there's no dependency on it, the issue arises with setCardInfo. In fetchLogins, when you call setCardInfo within a .then() block, the chain continues without waiting for the function to complete due to the lack of a Promise being returned by setCardInfo.


To resolve this, you have two options. Firstly, you can modify this.setCardInfo to return a Promise in its current structure. Alternatively, you can switch to using the cleaner and more readable async/await syntax, which essentially achieves the same result under the hood.

By marking a function as async, you're essentially wrapping its content in a return new Promise, where the resolved value of that function corresponds to the resolve value. This allows you to use .then with an asynchronous function. Moreover, utilizing await inside an asynchronous function enables sequential execution by pausing the function until the awaited task completes.

Another useful concept in this scenario is Promise.all, which waits for an array of Promises to resolve before resolving itself. By leveraging this, multiple asynchronous tasks can be executed concurrently and handled collectively upon completion.

Here is a proposed solution for the methods section:

methods: {
    fetchLogins: async function(language, location) {
      let res;
      try {
        res = await axios.get(
          `https://api.github.com/search/users?q=location:${location}+language:${language}&sort=followers&order=desc&page=${this.page}&per_page=8`,
          { headers: { Authorization: "90fa62d4dee8b02d363d83fccac86f3b7536492c" } }
        );
      } catch (err) {
        console.error(err);
      }

      console.log(res.data);
      this.loginList = res.data.items.map(item => item.login);
      console.log(this.loginList);

      this.cards = [];
      this.cardList = [];

      await this.setCardInfo();

      this.cards = this.cardList.sort((a, b) => b.followers - a.followers);
    },
    setCardInfo: async function () {
      this.cardList = await Promise.all(this.loginList.map(async login => {
        let res;
        try {
          res = axios.get(
            `https://api.github.com/users/${login}`,
            { headers: { Authorization: "90fa62d4dee8b02d363d83fccac86f3b7536492c" } }
          );
        } catch (err) {
          console.errror(err);
        }

        const user = res.data;
        const cardObject = {
          id: user.id,
          name: user.name,
          avatar: user.avatar_url,
          bio: user.bio,
          followers: user.followers,
          repositories: user.public_repos
        };

        return cardObject;
      }));
    },
  },

This revised implementation should maintain the functionality of your code while incorporating asynchronous operations for improved efficiency. If needed, feel free to refer to additional resources such as MDN's guide on asynchronous programming using async/await.

Answer №2

Avoid utilizing the newCardsArray variable and instead directly add the cardObject to your cardList. This will prevent assigning an empty value before the API call is completed.

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

What is the best way to add HTML tags both before and after a specified keyword within a string using JavaScript?

Imagine I have a string similar to this, and my goal is to add html tags before and after a specific keyword within the string. let name = "George William"; let keyword = "geo"; Once the html tags have been appended, the desired result should look like ...

Navigating a FormData object in javascript on a .NET WebAPI version 5.2.2

I am currently working on integrating webcam video recording upload using the example provided in this RecordRTC GitHub repo. However, I have encountered a compiling error when trying to use "Request.Files" as indicated in the screenshot below. The error ...

Having trouble getting Angular 2 animations to fade in

I've been trying to figure out how to make the html fadeIn for hours. Every time ngFor displays it, the opacity stays at 1 immediately, without fading in. FadingOut works fine, but the fadeIn effect is not working as expected. @Component({ selector:& ...

Guide on saving the token in local storage or cookies to grant access to specific web pages for users

Currently, I am in the process of developing an authentication system using Node.js, MySQL, and Express. Initially, I focused on saving and verifying user credentials in the database. Recently, I incorporated JWT (JSON Web Token) into the system. My next s ...

A customized Javascript application designed specifically for Blackberry Bold users, enabling them to effortlessly choose a country and seamlessly enter a correctly formatted zip code in the designated text field

I am currently developing a mobile web app for Blackberry Bold 9700 & 9650 devices running on the 5.0 browser. The main functionality of the app involves allowing users to select either US or Canada from a drop-down menu. Based on their selection, the ...

When importing a component that is passed as a prop in React, it is causing a blank page to render and is displaying an error in the

Visit this link for the code Everything was running smoothly with the app until I decided to include the Icon component in SidebarOption.js. Now, upon doing so, a blank page appears and an error is displayed in the console: An error occurred stating that ...

Is it possible to determine the number of JSON properties without the need for a loop?

I have a question about organizing data. I have a vast amount of data with various properties, and I am looking for a way to display each property along with how many times it occurs. For example: 0:[ variants:{ "color":"blue" "size":"3" } ] 1 ...

Chrome and Internet Explorer are not prompting to save passwords after the input type has been altered by a script

I've encountered an issue with a form that includes: <input type="password" id="password" /> I want to display some readable text temporarily, so I used the following code: $('#password').prop('type', 'text'); ...

Tips for retrieving the ID of a dynamic page

In my Higher Order Component (HOC), I have set up a function that redirects the user to the login page if there is no token. My goal is to store the URL that the user intended to visit before being redirected and then push them back to that page. However, ...

Uploading Images Dynamically with AJAX

Can someone assist me with a multiple upload form that utilizes AJAX to upload and display an image without the need for a submit button? My issue arises when dealing with repeating forms within table rows, causing only the first form to function properly. ...

Next.js: Dealing with special characters in YouTube video API snippet titles

Trying to find the perfect video snippet title without any special characters. Accessing the API: https://www.googleapis.com/youtube/v3/search, along with specifying snippet. The current output for snippet.title is as follows: I&#39;M GONNA CARRY ...

Is there a way to prevent tinymce from automatically inserting <!DOCTYPE html><html><head></head><body> before all my content?

I have integrated TinyMCE as the editor for one of my database fields. The issue I am encountering is that when I input the text "abc" into the editor, it gets saved in the database surrounded by unnecessary HTML elements. This is the structure currently s ...

What is the best way to display a child component for every object while considering specific conditions?

I find myself wanting to use both v-for and v-if together, even though I know it's not doable. In a nutshell, my goal is to display a child component for each item in a prop. However, I also need to extract specific data from these items in order to ...

Automatic line breaks in MathJax when displayed in a modal dialogue box

As part of a math project, I need to display the solution of a problem in a Sweetalert2 modal. However, despite using the following code: <script type="text/x-mathjax-config"> MathJax.Hub.Config({ tex2jax: { inlineMath: [['$','$ ...

Ways to access and delete the canvas using ref steps?

Having a canvas in a react component, I utilized react refs to access the node and implemented a destroy() method. However, I encountered an error: TypeError: canvasRef.current.destroy is not a function How can we properly access the node (canvas) using r ...

Having trouble understanding how to display an HTML file using Next.JS?

Currently, I am working on a project that involves utilizing Next.JS to develop a webpage. The main goal is to display a PDF file on the left side of the screen while integrating Chaindesk on the right side to create a chat bot capable of extracting inform ...

Getting Javascript as a string using Selenium in Python

Is it possible to retrieve JavaScript code using Python Selenium? Specifically, I want the JS code as a string. function validateForm() { var x = document.forms["myForm"]["Password"].value; if (x.length >= 6) { } } ...

What is the proper way to invoke express-validator within a middleware function?

I am facing a challenge in invoking the express-validator function from a middleware function. Although I can see that the execution is happening within the express-validator, validation does not seem to occur. The code snippet is provided below: router.g ...

Having trouble displaying an image in p5.js on Visual Studio Code

I can't seem to figure out how to load images in visual studio code for p5.js. Can someone help me understand the process of loading images in p5.js? I've set up a new project, but the images I try to load are not displaying correctly. How can I ...

Struggling with TypeScript errors when using Vue in combination with Parcel?

While running a demo using vue + TypeScript with Parcel, I encountered an error in the browser after successfully bootstrapping: vue.runtime.esm.js:7878 Uncaught TypeError: Cannot read property 'split' of undefined at Object.exports.install ...