反应钩子,在批量处理执行了多个promise后更新

问题描述

我正在研究一个读取CSV文件并在文件上的每一行发送请求的组件。该过程运行良好,但是随着线路成功发布,我试图显示反馈。问题在于,使用useState挂钩时,set函数调用函数时即通过,而不是在每个promise已解决之后才传递。因此,我无法追加到成功的结果数组中,该数组不断被上次成功调用替换。 为了避免服务器过载,API调用会被消退一秒钟。

import React,{useState} from "react";
import CSVReader from "react-csv-reader";
import {post} from "../api";

function App() {
    const [inserts,setInserts] = useState([])

    const callApi = async (x) => {
        let item = {
            date: x.date,value: x.value,};

        await post(`add-items`,item);
        setInserts([...inserts,item])

    };

    const debouncedApiCall = (body,delay) => {
        return new Promise((resolve) => {
            const handler = () => callApi(body).then((x) => resolve(x));
            setTimeout(handler,delay);
        });
    };

    const insert = async (rows) => {
        let timer = 0;
        await Promise.all(
            rows.map(async (x) => {
                timer++;
                return await debouncedApiCall(x,timer * 1000);
            })
        );
    };

    let onFileLoaded = (data) => {
        insert(data).then((x) => console.log(x));
    };

    return (
        <div>
            <CSVReader  onFileLoaded={onFileLoaded}/>
            {JSON.stringify(inserts)}
        </div>
    );
}

export default App;

解决方法

调用呼叫API函数时,在其闭包内捕获inserts的状态。这意味着inserts并不总是最新的。最终得到的结果称为“ stale closure”。

要解决此问题,useState方法提供的变异函数可以接受回调函数。调用函数时,此回调函数可以接收最新状态。这对异步操作很有帮助。

您的callApi函数将变为

const callApi = async (x) => {
    let item = {
        date: x.date,value: x.value,};

    await post(`add-items`,item);
    setInserts(prevState => [...prevState,item]) //prevState gets the latest state of inserts when setInserts is called
    return (x); //will return this value once this async function finishes. similar to resolve(x)
};

我无法完全调试您的代码,但我认为这是不必要的步骤。您应该可以更改插入函数以等待所有callApi的调用,并且只需从callApi函数返回x(如我在上面添加的内容)即可。

const insert = async (rows) => {
    let timer = 0;
    await Promise.all(
        rows.map((x) => {
            return callApi(x);  //Promise.All wants an array of promises. async functions return a promise
        })
    );
};

作为旁注,Promise.all返回一个promise,其中包含所有promise结果的实际数组结果。您可以通过在Promise.All上添加.then来获取它们,然后从insert函数中删除异步,或等待结果。

基于异步:插入会返回一个承诺,因此您需要在调用函数中进行处理。

const insert = async (rows) => {
    let timer = 0;
    const results = await Promise.all(
        rows.map((x) => {
            return callApi(x);  //Promise.All wants an array of promises. async functions return a promise
        })
    );
    return results; //array of all your x values for each row
};

基于非异步:行尾,insert是调用函数

const insert = (rows) => {
    let timer = 0;
    Promise.all(
        rows.map((x) => {
            return callApi(x);  //Promise.All wants an array of promises. async functions return a promise
        })
    ).then((result) => {
    //result is an array of all x values according to rows
    });
};