是否可以对每个承诺使用axios.all和then()?

时间:2017-04-27 17:40:53

标签: javascript promise axios

我有一个React组件,它触发一个事件来获取数据。这导致动态数量的存储过程调用以获取数据,并且来自每个调用的数据存储在完全不同的位置。然后,一旦收到所有数据并且可用,我需要重新渲染。我正在使用axios承诺。

由于axios调用的数量是动态的,我正在构建一个数组并将其插入axios.all,如下所示:

let promises = [];

for (let i = 0; i < requests.length; i++) {
    promises.push(axios.get(request[i].url, { params: {...} }));
}

axios.all(promises).then(/* use the data */);

问题是每个axios请求都会返回在完全不同的位置添加到对象的数据。由于我无法将它们全部放在一个then中的正确位置(我怎么知道哪个响应在哪个位置?),我尝试这样做:

let promises = [];

for (let i = 0; i < requests.length; i++) {
    promises.push(
        axios.get(request[i].url, { params: {...} })
            .then(response => {myObject[request[i].saveLocation] = response.data;})
    );
}

axios.all(promises).then(/* use the data */);

然而,这并不像我预期的那样有效。执行每个thenget,但直到then附加到axios.all之后。显然这是一个问题,因为我的代码在将数据保存到对象之前尝试使用它。

是否可以对每个then调用一个单独的axios.get调用,该调用将在解析相应的promise后执行,然后生成一个仅执行then的{​​{1}}在所有承诺得到解决后,现在使用数据已经填充了对象?

4 个答案:

答案 0 :(得分:24)

好的,所以我找到了一种方法来执行我需要的操作,而无需在每个then上使用get。由于传入axios.get的参数包含足够的信息来确定保存位置,并且因为我可以从响应中读取参数,所以我可以执行以下操作:

let promises = [];

for (let i = 0; i < requests.length; i++) {
    promises.push(axios.get(request[i].url, { params: {...} }));
}

axios.all(promises)
    .then(axios.spread((...args) => {
        for (let i = 0; i < args.length; i++) {
            myObject[args[i].config.params.saveLocation] = args[i].data;
        }
    }))
    .then(/* use the data */);

这可确保在使用之前接收所有数据并将其保存到对象中。

答案 1 :(得分:2)

如果您的第二次尝试的行为确实如此,那么这将表明axios不符合Promise / A +。 then回调的返回值必须是履行then返回的承诺的值。由于这是您推入数组的承诺,因此axios.all将为该承诺返回的值只能通过首先执行then回调来获知。

事件虽然您没有在then回调中明确返回值,但这不会影响上述规则:在这种情况下,返回值为undefined 一旦相应的承诺得到解决,应由axios.all提供的值。

特别参见规则2.2.7,2.2.7.1,2.3.2.1,2.3.2.2 in the specs of Promise/A+)。

所以我建议使用Promise / A +兼容的promise实现。还有其他几个库,例如request-promise

或者,您可以使用本机ES6 Promise实现,并promisify the http.request method自己使用。

ES6提供Promise.all,保证以与提供的承诺相同的顺序提供已解决的值。

答案 2 :(得分:0)

如果您将promise传递给带有各自then函数的数组,则您的初始代码可以按预期正常工作

let promises = []; // array to hold all requests promises with their then
for (let i = 0; i < requests.length; i++) {
    // adding every request to the array
    promises.push(
        axios.get(request[i].url, { params: { ...} })
            .then(response => { myObject[request[i].saveLocation] = response.data; })
    );
}
// Resolving requests with their callbacks before procedding to the last then callback
axios.all(promises).then(/* use the data */);

答案 3 :(得分:0)

似乎在此发布日,axios建议使用Promise.all而不是axios.all https://github.com/axios/axios 这就是Nuxtjs对我有用的

async nuxtServerInit(vuexContext, context) {
    console.log(context);

    const primaryMenuData = {
      query: `query GET_MENU($id: ID!) {
        menu(id: $id, idType: NAME) {
          count
          id
          databaseId
          slug
          name
          menuItems {
            edges {
              node {
                url
                label
                target
              }
            }
          }
        }
      }`,
      variables: {
        "id": "Primary"
      }
    }

    const primaryMenuOptions = {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      data: primaryMenuData,
      url: 'http://localhost/graphql'
    };

    const postsData = {
      query: `query GET_POSTS($first: Int) {
        posts(first: $first) {
          edges {
            node {
              postId
              title
              date
              excerpt
              slug
              author {
                node {
                  name
                }
              }
              featuredImage {
                node {
                  altText
                  caption
                  sourceUrl(size: MEDIUM)
                }
              }
            }
          }
        }
      }`,
      variables: {
        "first": 15
      }
    }

    const postsOptions = {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      data: postsData,
      url: 'http://localhost/graphql'
    };
        
    try {
      const [primaryMenuResponse, postsResponse] = await Promise.all([
        await axios(primaryMenuOptions),
        await axios(postsOptions)
      ])
      
      vuexContext.commit('setPrimaryMenu', primaryMenuResponse.data.data.menu.menuItems.edges);
      vuexContext.commit('setPosts', postsResponse.data.data.posts.edges);

    } catch (error) {
      console.error(error);
    }      
  },