VueJS中与GET请求的异步性

时间:2020-08-21 03:18:41

标签: javascript vue.js http asynchronous axios

我还是VueJS的新手。因此,我正在尝试向git API发出GET请求。首先,我提出了一个按照关注者数量对用户进行排序的请求,该请求以用户登录的降序检索出一个数组。之后,我对每个登录值都发出另一个GET请求,以获取更深入的数据,例如头像url,存储库计数等。问题是,而且我通常仍在处理异步问题,因为某些请求的执行速度比其他情况下,我的新对象数组在关注者数量方面变得混乱。我正在尝试执行sort()方法,但似乎无法弄清在js流中运行每个函数的顺序。有人可以帮我吗?

这是我正在使用的两个组件:

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 = [];
      // Zera o state de cards e itera o array loginList fazendo um GET request e criando o objeto para o array cards
      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);
          })
      );
      // Por causa da assincronicidade, alguns objetos, mesmo com mais seguidores, acabam ficando atrás na ordem do array
      // invoco um sort() em ordem descendente
      this.cardList = newCardsArray;
    },
  },
};
</script>

和我的SearchBars组件


<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>

2 个答案:

答案 0 :(得分:0)

之所以不起作用,是因为您的两个函数都不是异步的。对fetchLogins而言,这无关紧要,因为没有任何等待。但是,由于在fetchLogins中,您正在等待对setCardInfo的调用,因此需要setCardInfo是异步的。发生的是这一行:

.then(() => this.setCardInfo())

您的.then链会启动this.setCardInfo,但不会等到转移到链中的下一个.then之前。这是因为setCardInfo不会返回Promise,所以.then立即解决。


有两种方法可以做到这一点。您可以保持代码的当前组织方式,并使this.setCardInfo返回Promise;要么;您可以更改为async / await的语法(实际上是相同的),并使您的代码更具可读性。

定义async函数时,您真正要做的只是将其包装在return new Promise中,该函数的返回值是resolve的值。因此,这意味着您可以.then使用该功能。 但是,等一下!!当一个函数异步时,您可以使用特殊关键字await来告诉该函数“运行此行,等待其结束,然后继续前进” -实际上,您正在做的是.then-其余的功能,而不必链接另一个.then并添加另一个级别压痕。

在您的情况下,您可以使用的另一件事是Promise.all:您向它传递了一组Promises(据我们所知,async函数在幕后返回了!),并且只有在阵列中的所有功能都解决后,它才能解决承诺。因此,您可以做类似的事情,

async function myFunc(arr) {
  const responses = await Promise.all(arr.map(async item => {
    const res = await fetch('api', { body: item });
    return await res.json();
  });
}

,最后得到的是arr项的响应的JSON数组。它所做的只是创建未解决的承诺数组,而Promise.all只是等待它们全部解决。如果其中任何一个拒绝,则Promise.all将拒绝。因此,当我在这里使用该范例时,我将其放在try中的catch / map中,因此希望您可以逐张处理它。 >

看看我提出的解决方案(这只是methods部分):

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);
        // deal w/ error, so the code below (dependent
        // on 'res') doesn't run. Or just let it err out.
        // up to you.
      }
      
      // None of this is async, so it doesn't need an await
      console.log(res.data);
      this.loginList = res.data.items.map(item => item.login);
      console.log(this.loginList);

      // Run the other function
      this.cards = [];
      this.cardList = [];

      // Keeping this as a separate function, since I don't know how you're
      // going to use it, but it could totally be be copied here.
      // "this.cardList // = await Promise.all ..." is really just one
      // line down there. Paste it here if you aren't gonna need it as a
      // separate function
      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);
          // Deal w/ error (return undefined? "couldn't find user's info"?)
        }

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

        // Resolve the Promise for this iteration of the map funciton
        return cardObject;
      }));
    },
  },

我不是100%确信这对您有用,但是我认为我并未更改代码实际执行的任何操作,因此应该更改。如果没有,我希望对Promises和async / await的解释能成为踏脚石。

我建议您查看非常棒的MDN,以获取有关此资源的信息。他们甚至有一个starter guide您可以阅读,因此您不必简单地阅读正式文档。

答案 1 :(得分:0)

尽量不要使用newCardsArray变量,只需将cardObject推入cardList即可。 在API调用完成之前,它正在分配空值。