【问题标题】:async function triggers setstate before await is completeasync 函数在等待完成之前触发 setstate
【发布时间】:2020-07-17 01:24:12
【问题描述】:

setLoading 在获取所有数据之前被调用。如何确保仅在获取数据的 for 循环完成后才调用 setLoading?将异步数据与非异步数据结合起来甚至是可取的吗?我想要实现的是呈现一个时间线,每个事件都伴随着它们各自的方向(这是异步数据)

const Schedule = (props) => {
const [events, setEvents] = React.useState([]);
const [visible, setVisible] = React.useState(false);
const [unsatisfied, setUnsatisfied] = React.useState("");
const [timingsArray, setTimingsArray] = React.useState([]);
const [isLoading, setLoading] = React.useState(true);


React.useEffect(() => {
            directionsArray(props.initRoutes, props.timings, props.data);
        }, []);

const directionsArray = async (allRoutes, timings, data) => {
    let result = [];

    for (let i = 0; i < allRoutes.length - 1; i++) {
        let obj = { distance: "", duration: "", steps: [] };
        let steps = [];
        let distance = "";
        let duration = "";
        let origin =
            typeof allRoutes[i] === "object"
                ? allRoutes[i].lat + "," + allRoutes[i].long
                : allRoutes[i];
        let destination = allRoutes[i + 1];
        try {
            let resp = await fetch(
                "https://maps.googleapis.com/maps/api/directions/json?origin=" +
                    origin +
                    "&destination=" +
                    destination +
                    "&key=" +
                    GOOGLE_MAPS_API_KEY +
                    "&mode=transit&region=sg"
            );
            //console.log(JSON.stringify(await resp.json()));
            let data = (await resp.json())["routes"][0]["legs"][0];
            let response = data["steps"];
            distance = data["distance"]["text"];
            duration = data["duration"]["text"];

            for (let j = 0; j < response.length; j++) {
                steps.push(await routeFormatter(await response[j]));
            }
        } catch (err) {
            console.log(err);
        }
        obj.steps = steps;
        obj.distance = distance;
        obj.duration = duration;
        result.push(obj);
    }
    let updatedTimings = merge(timings, result);
    let combinedData = eventsWithDirections(updatedTimings, data, result);
    setTimingsArray(updatedTimings);
    setEvents(combinedData);
    setLoading(false);
};

if (isLoading) {
        return (
            <View
                style={{
                    flex: 1,
                    alignContent: "center",
                    justifyContent: "center",
                }}
            >
                <ActivityIndicator
                    style={{ alignSelf: "center" }}
                    size="large"
                />
            </View>
        );
    } else {
        return (
            <View style={styles.container}>
                <View style={styles.body}>
                    <Modal animated visible={visible} animationType="fade">
                        <ActionOptions
                            onReselect={onReselect}
                            onClose={onClose}
                            unsatisfied={unsatisfied}
                            events={props.allEvents}
                            genres={props.genres}
                            newTimeChange={newTimeChange}
                            filters={props.filters}
                        />
                    </Modal>
                    <Timeline
                        onEventPress={(event) => onEventPress(event)}
                        data={events}
                        timeStyle={{
                            textAlign: "center",
                            backgroundColor: "#cc5327",
                            color: "white",
                            padding: 5,
                            borderRadius: 13,
                        }}
                      />
                </View>
                <View style={styles.footer}>{renderProceedButton()}</View>
            </View>
        );
    }
};

【问题讨论】:

  • 不清楚这里有什么问题。在所有这些 set 调用 after 之前,似乎什么都不会发生。 setLoading 在数组准备好之前被调用是什么意思?
  • @JaredSmith 我将获取的数据推送到结果数组中,然后我想使用该数组来呈现我的组件,但是在数组具有来自 API 调用的数据之前调用 setLoading组件什么也不渲染。
  • 再说一次,我不知道是否存在语言障碍或概念障碍或两者兼而有之,但这根本不是这样的。
  • 尝试在setEvents(combinedData); 之前记录combinedData 以确保它是您所期望的
  • @RuiFeng 你可以使用Promise.all

标签: javascript reactjs react-native async-await


【解决方案1】:

我认为这里的问题是遍历allRoutes 的循环需要包装在Promise.all 中。现在,在循环的每次迭代中,try 块中的其余代码将等待获取数据,但 setLoading 在该范围之外被调用,并且不会被告知 await 任何东西否则。

而不是 for 循环,也许尝试让迭代类似于

await Promise.all(allRoutes.map( (route) => {
  //code that's currently in the for loop
}))

希望这是有道理的。我发现这篇文章对主题也很有帮助:https://zellwk.com/blog/async-await-in-loops/

【讨论】:

    猜你喜欢
    • 2016-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-23
    相关资源
    最近更新 更多