我有一个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 */);
然而,这并不像我预期的那样有效。执行每个then
后get
,但直到then
附加到axios.all
之后。显然这是一个问题,因为我的代码在将数据保存到对象之前尝试使用它。
是否可以对每个then
调用一个单独的axios.get
调用,该调用将在解析相应的promise后执行,然后生成一个仅执行then
的{{1}}在所有承诺得到解决后,现在使用数据已经填充了对象?
答案 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);
}
},