不在for循环中使用await axios请求吗?

huangapple go评论73阅读模式
英文:

Doesn't await axios requests when i use in for loop?

问题

我正在尝试从后端通过产品ID获取productInfo。我正在遍历产品ID并使用axios获取数据。但它每次编译只迭代一个元素。如何在一次执行中迭代它们所有?

const useBasket = (location) => {
    const [products, setProducts] = useState([]);
    const [descriptions, setDescriptions] = useState([]);
    console.log("ACCOUNT ID:", location.location.state.accountId);
    useEffect(() => {
        const getBasket = async () => {
            const basketResponse = await Axios.get("http://localhost:8084/accountId/" + location.location.state.accountId);
            setProducts(basketResponse.data.products);
        };
        const getProductInfo = async (props) => {
            console.log("INSIDE getProductInfo:", props);
            const productResponse = await Axios.get("http://localhost:8081/product/" + props).catch(err => console.log(err));
            setDescriptions([...descriptions, productResponse.data.description]);
        }
        getBasket();
        for (let i = 0; i < products.length; i++) {
            console.log("INSIDE FOR:", products[i]);
            getProductInfo(products[i]);
        }

    }, []);
    console.log("DESCRIPTIONS:", descriptions);
    return { descriptions };
};

我尝试将for循环放在一个异步函数中。然后在for循环内部进行函数调用时使用await。

当数据不完整时,我还添加了加载部分,以防止返回不完整的数据。

但它们都没有起作用。

英文:

I am trying to get productInfo by productId from backend. I am iterating over productId and fetching data with axios. But it only iterates one element for every compile. How can i iterate all of them in one execution ?

const useBasket = (location) =&gt; {
    const [products, setProducts] = useState([]);
    const [descriptions, setDescriptions] = useState([]); 
    console.log(&quot;ACCOUNT ID:&quot;, location.location.state.accountId);
    useEffect(() =&gt; {
        const getBasket = async () =&gt; {
            const basketResponse = await Axios.get(&quot;http://localhost:8084/accountId/&quot; + location.location.state.accountId);
            setProducts(basketResponse.data.products);
        };
        const getProductInfo = async (props) =&gt; {
            console.log(&quot;INSIDE getProductInfo:&quot;,props);
            const productResponse = await Axios.get(&quot;http://localhost:8081/product/&quot; + props).catch(err =&gt; console.log(err));
            setDescriptions([...descriptions,productResponse.data.description]);  
        }
        getBasket();
        for(let i = 0; i &lt; products.length; i++){
            console.log(&quot;INSIDE FOR:&quot;,products[i]);
            getProductInfo(products[i]);
        } 
        
    }, []); 
    console.log(&quot;DESCRIPTIONS:&quot;,descriptions);
    return { descriptions }; 
};

I tried to put the for loop inside of a async function. After that i made await the function call inside of the for loop.

I also put loading section when data is not complete it won't return.

But none of them worked.

答案1

得分: 3

State updates are asynchronous and the closure captures the current value of each of your state variables (which won't change inside the same closure). You should directly access the values from the response instead and use await to ensure the request for products finishes before getting the descriptions.

Here is one way to implement it:

const getBasket = async () => {
	const basketResponse = await Axios.get("http://localhost:8084/accountId/" + location.location.state.accountId);
	return basketResponse.data.products;
};
const getProductInfo = (props) => {
	return Axios.get("http://localhost:8081/product/" + props).then(r => r.data.description);
}
(async () => {
    const newProducts = await getBasket();
    setProducts(newProducts);
    const newDescriptions = await Promise.all(newProducts.map(getProductInfo));
    setDescriptions([...descriptions, ...newDescriptions]);
})();
英文:

State updates are asynchronous and the closure captures the current value of each of your state variables (which won't change inside the same closure). You should directly access the values from the response instead and use await to ensure the request for products finishes before getting the descriptions.

Here is one way to implement it:

const getBasket = async () =&gt; {
	const basketResponse = await Axios.get(&quot;http://localhost:8084/accountId/&quot; + location.location.state.accountId);
	return basketResponse.data.products;
};
const getProductInfo = (props) =&gt; {
	return Axios.get(&quot;http://localhost:8081/product/&quot; + props).then(r =&gt; r.data.description);
}
(async () =&gt; {
    const newProducts = await getBasket();
    setProducts(newProducts);
    const newDescriptions = await Promise.all(newProducts.map(getProductInfo));
    setDescriptions([...descriptions, ...newDescriptions]);
})();

答案2

得分: 0

getBasket 是一个异步函数。执行不会等待它完成/返回再继续。这意味着你的 for 循环正在引用一个产品状态变量,这个变量可能尚未被更新。

有几种解决方法(假设你不能简单地修改产品获取以包括描述信息):

  • 你可以将 getBasket 包装在一个 promise 中,并在迭代之前等待它。
  • 你可以将 getProductInfo 函数与 getBasket 结合起来,然后迭代 basketResponse.data.products
  • 你可以将 getProductInfo 包装在自己的 useEffect 中,当产品更新时执行,例如:
useEffect(() => {
    const getProductInfo = async (props) => {
        const productResponse = await Axios.get("http://localhost:8081/product/" + props).catch(err => console.log(err));
        setDescriptions((prev) => [...prev, productResponse.data.description]);  
    }
                    
    for(let i = 0; i < products.length; i++){
        getProductInfo(products[i]);
    }
}, [products]);
英文:

getBasket is an async function. Execution does not wait for it to finish/return before continuing. This means your for loop is referencing a products state variable this is unlikely to have been updated.

There are a number of ways around this (assuming you cannot simply modify the product fetch to include descriptions):

  • You could wrap getBasket in a promise and await it before iterating.

  • You could combine your getProductInfo function within getBasket and iterate basketResponse.data.products

  • You could wrap getProductInfo in its own useEffect which is executed when products are updated i.e.

    useEffect(() =&gt; {
    
       const getProductInfo = async (props) =&gt; {
          const productResponse = await Axios.get(&quot;http://localhost:8081/product/&quot; + props).catch(err =&gt; console.log(err));
          setDescriptions((prev) =&gt; [...prev, productResponse.data.description]);  
      }
    
      for(let i = 0; i &lt; products.length; i++){
          getProductInfo(products[i]);
      }
    }, [products]);
    

答案3

得分: 0

以下是翻译好的部分:

有一些可以更改的地方,它们都涉及如何处理不会立即发生的事情,比如:

  1. 调用发送API请求的异步函数(你等待axios返回值,但不等待整个异步函数完成,所以在请求完成之前,for循环部分开始执行)。
  2. 在setState之后使用状态(这不是瞬时的,setState调用会被批量执行在一起,它很快,只有在写使用状态的代码之后才会注意到它)。

现在,可能有不同的解决方法,但以下是改动最少的解决方案:

const useBasket = (location) => {
    const [products, setProducts] = useState([]);
    const [descriptions, setDescriptions] = useState([]);
    console.log("ACCOUNT ID:", location.location.state.accountId);

    const getBasket = async () => {
        const basketResponse = await Axios.get("http://localhost:8084/accountId/" + location.location.state.accountId);
        setProducts(basketResponse.data.products);
        return basketResponse.data.products;
    };
    const getProductInfo = async (props) => {
        console.log("INSIDE getProductInfo:", props);
        const productResponse = await Axios.get("http://localhost:8081/product/" + props).catch(err => console.log(err));
        setDescriptions([...descriptions, productResponse.data.description]);
    }

    useEffect(async () => {
        const prods = await getBasket();
        for (let i = 0; i < prods.length; i++) {
            console.log("INSIDE FOR:", prods[i]);
            getProductInfo(prods[i]);
        }

    }, []);

    console.log("DESCRIPTIONS:", descriptions);
    return { descriptions };
};
  1. getBasketgetProductInfo 被移到了 useEffect 外部(可选,为了可读性)。
  2. getBasket 在完成时也返回数据。
  3. getBasket 调用之前添加了 await
  4. for 循环使用返回的列表而不是从状态中获取。
英文:

There are a few things that can be changed here, and they all boil down to how to handle stuff that doesn't happen immediately, like:

  1. calling async functions that send api requests (you do wait for axios to return the value, but you don't wait for the whole async function to finish, so then the for loop part starts executing before the request is finished)
  2. using state after setState (this isn't instantaneous, setState calls are batched and executed together, it's so quick that the only time you notice it when you write code that uses the state after a setState call)

Now, there might be different ways to solve this, but here's the solution with least changes:

const useBasket = (location) =&gt; {
    const [products, setProducts] = useState([]);
    const [descriptions, setDescriptions] = useState([]);
    console.log(&quot;ACCOUNT ID:&quot;, location.location.state.accountId);
    
    const getBasket = async () =&gt; {
        const basketResponse = await Axios.get(&quot;http://localhost:8084/accountId/&quot; + location.location.state.accountId);
        setProducts(basketResponse.data.products);
        return basketResponse.data.products;
    };
    const getProductInfo = async (props) =&gt; {
        console.log(&quot;INSIDE getProductInfo:&quot;, props);
        const productResponse = await Axios.get(&quot;http://localhost:8081/product/&quot; + props).catch(err =&gt; console.log(err));
        setDescriptions([...descriptions, productResponse.data.description]);
    }

    useEffect(async () =&gt; {
        const prods = await getBasket();
        for (let i = 0; i &lt; prods.length; i++) {
            console.log(&quot;INSIDE FOR:&quot;, prods[i]);
            getProductInfo(prods[i]);
        }

    }, []);

    console.log(&quot;DESCRIPTIONS:&quot;, descriptions);
    return { descriptions };
};
  1. the getBasket and getProductInfo were moved outside the useEffect (optional, for readability)
  2. getBasket also returns the data when it is done
  3. an await was added before the getBasket call
  4. the for loop uses the returned list instead of the one from state

huangapple
  • 本文由 发表于 2023年6月15日 04:35:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/76477348.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定