首页 > 解决方案 > ReactJS - axios.post 的同步问题

问题描述

我在 reactjs 中的 axios.post 调用存在同步问题。我有一个输入区域供用户选择要提交到 axios.post 的本地文件,该文件被翻译成数组缓冲区,然后通过 axios 调用发送到 microsoft faces api。如果我选择一个图像,然后按分析按钮,同步将关闭,它会尝试在没有数组缓冲区的情况下发出发布请求。如果我第二次单击它,它会正确发布帖子。

我对等待和异步还很陌生,对我的问题的任何输入都会有很大的帮助。我考虑过使用布尔标志,但想了解如何使用正确的设计来改进此功能。

import React, { useState } from "react";
import axios from "axios";
import { Button, Container, Row, Col } from "react-bootstrap";
import "../../styling/FacialRecognition.css";
import defaultImage from "../../assets/images/defaultImage.png";

function FacialRecognition() {
    const [image, setImage] = useState("");
    const [imageData, setImageData] = useState([]);
    const [responseData, setResponseData] = useState([]);
    const [dataLoad, setDataLoad] = useState(false);


//translates the local file to the array buffer, and sets the imageData.
    function TranslateImageToArrayBuffer(imageToTranslate) {
        let reader = new FileReader();
        reader.readAsArrayBuffer(imageToTranslate);
        reader.onloadend = () => {
        setImageData(reader.result);
        };
    }


//Makes the call with the url,options, and the arraybuffer.
    function ProcessImage(localImage) {
        TranslateImageArrayBuffer(localImage);
        console.log("ImageData: " + imageData);
      
        var subscriptionKey = "<subscriptionkey>";
        var uriBase =
            "https://(urlgoeshere).cognitiveservices.azure.com/face/v1.0/detect";
        const options = [
            "returnFaceId=true",
            "returnFaceLandmarks=true",
            "returnFaceAttributes=age,gender,headPose,smile,facialHair,glasses,emotion,hair,makeup,accessories",
        ];

        uriBase = uriBase.concat("?", options.join("&"));
        // Perform the REST API call.

        axios
            .post(uriBase, imageData, {
                headers: {
                    "Content-Type": "application/octet-stream",
                    "Ocp-Apim-Subscription-Key": subscriptionKey,
                },
            })
            .then(function (response) {
                // Display the image.

                var blob = new Blob([imageData]);
                var url = URL.createObjectURL(blob);
                setDataLoad(true);
                document.querySelector("#sourceImage").src = url;
                console.log("Status text: " + response.status);
                console.log("Status text: " + response.statusText);

                console.log(response.data);
                setResponseData(response.data);
            })
            .catch(function (error) {
                console.log(error);
            });
    }

    let value = responseData.map((e) => JSON.stringify(e, null, 2));
    return (
        <div>
            <Container fluid="xl">
                <Row>
                    <Col xs="auto">
                        <p>
                            Select a local image, and then click the <b>Analyze face button.</b>
                        </p>
                    </Col>
                </Row>
                <Row>
                    <Col x="auto">
                        <label>Image to analyze: </label>
                        <input
                            type="file"
                            name="inputImage"
                            id="inputImage"
                            autoComplete="off"
                            accept="image/png, image/jpeg"
                            onChange={(e) => setImage(e.target.files[0])}
                            style={{ width: "60%", marginLeft: "1vw" }}
                        />
                        <Button
                            variant="info"
                            onClick={() => ProcessImage(image)}>
                            Analyze face
                        </Button>
                    </Col>
                </Row>
                <Row style={{ marginTop: "2vw" }}>
                    <Col xs="6">
                        <label>
                            <strong>Response</strong>
                        </label>
                    </Col>
                    <Col xs="auto">
                        <label>
                            <strong>Source Image</strong>
                        </label>
                    </Col>
                </Row>
                <Row>
                    <Col>
                        {dataLoad ? (
                            <textarea
                                id="responseTextArea"
                                className="UIInput"
                                style={{ width: "100%", height: "429px" }}
                                value={value}
                                readOnly={true}
                                disabled="disabled"></textarea>
                        ) : (
                            <textarea
                                id="responseTextArea"
                                className="UIInput"
                                style={
                                    dataLoad
                                        ? { display: "none" }
                                        : { width: "100%", height: "auto" }
                                }
                                defaultValue="This field will display analyzed data on the image"
                                readOnly={true}
                                disabled="disabled"></textarea>
                        )}
                    </Col>
                    <Col xs="6">
                        {dataLoad ? (
                            <img
                                id="sourceImage"
                                alt=""
                                style={
                                    dataLoad
                                        ? { width: "80%" }
                                        : { display: "none" }
                                }
                            />
                        ) : (
                            <img
                                id="defaultImage"
                                src={defaultImage}
                                alt=""
                                style={
                                    dataLoad
                                        ? { display: "none" }
                                        : { width: "80%" }
                                }
                            />
                        )}
                    </Col>
                </Row>
            </Container>
        </div>
    );
}

export default FacialRecognition;

我也尝试设置TranslateImageToArrayBuffer为异步函数,并将 axios 调用放入 a 中.then(response => {axios.post...}),但没有运气。

编辑:我还尝试将我的翻译功能内容包装在一个承诺中。

async function TranslateImageToArrayBuffer(imageToTranslate) {
    return new Promise((resolve, reject) => {
        var reader = new FileReader();

        reader.onload = () => {
            setImageData(reader.result);
            resolve(imageData);
        };

        reader.onerror = () => {
            reject(reader.result);
        };
        reader.readAsArrayBuffer(imageToTranslate);
    });
}

并在我的 ProcessImage 函数中使用const contents = await TranslateImageToArrayBuffer(localImage);一行来等待该值。仍然有同步问题。

标签: reactjsasync-awaitaxiosreact-hooks

解决方案


我无法让它与异步一起工作,所以我将我的函数 onChange 用于输入文件以将图像转换为数组缓冲区,而不是在ProcessImage().

                    <input
                        type="file"
                        name="inputImage"
                        id="inputImage"
                        autoComplete="off"
                        accept="image/png, image/jpeg"
                        onChange={(e) =>{ TranslateImageToArrayBuffer(e.target.files[0]);setImage(e.target.files[0]) }}
                        style={{ width: "60%", marginLeft: "1vw" }}
                    />
                    <Button
                        variant="info"
                        onClick={() => ProcessImage()}>
                        Analyze face
                    </Button>

这绕过了进行异步设置的需要。


推荐阅读