首页 > 解决方案 > 如何在不取消 API 调用的情况下使用 axios 设置超时?

问题描述

一点背景知识,我有这个 API 调用,它的响应可能很长(在某些情况下谈论超过一分钟,但大部分时间为 10-15 秒)。我想做的是在后端继续处理呼叫时在客户端设置超时。我正在使用 axios 来处理 http 请求,并且我知道有一个timeout默认键0意味着没有超时,因此调用将继续,直到成功或失败。我试图将其设置1为看看这将如何处理一毫秒的超时并取消呼叫......这是有道理的。我现在的问题是,如何在不取消 HTTP 请求的情况下在客户端实现超时?一些代码可以让您了解我尝试过的内容。

import React from "react";
import axios from "axios"

function App() {
  const fetchLongRequest = async () => {
    try{

// All peachy over here if no timeout is implemented...

      const myRequest = await axios({
        url: "https://jsonplaceholder.typicode.com/todos/1",
        headers: {
          accept: "application/json",
          "Content-Type": "application/json"
        },
      })
      console.log("SUCCESS!", JSON.stringify(myRequest.data, null, 2))
    }catch(error){
      console.log("FAIL!", error.message)
    }
  }

  return (
   <button onClick={() => fetchLongRequest()}>Fetch</button>
  );
}

export default App;

现在这是我对超时的介绍

import React from "react";
import axios from "axios";

function App() {
  const fetchLongRequest = async () => {

// timeout works as expected but I'd like to let the call go to the backend and do its thing. 

    try {
      const myRequest = await axios({
        url: "https://jsonplaceholder.typicode.com/todos/1",
        headers: {
          accept: "application/json",
          "Content-Type": "application/json",
        },
        timeout: 1,
      });
      console.log("SUCCESS!", JSON.stringify(myRequest.data, null, 2));
    } catch (error) {
      console.log("FAIL!", error.message);
    }
  };

  return <button onClick={() => fetchLongRequest()}>Fetch</button>;
}

export default App;

我知道这个请求有点奇怪,因为它引发了许多问题,例如错误处理、如何知道这个调用何时完成等等。我想得到一些关于如何完成这个任务的反馈......请:)

标签: javascriptreactjsaxios

解决方案


您只需要在请求之前设置一个超时

import React from "react";
import axios from "axios";

function App() {
    const fetchLongRequest = async () => {
        
        const waitTime = 5000;
        setTimeout(() => console.log("Request taking a long time"), waitTime);
        try {
            const result = await axios({
                url: "https://jsonplaceholder.typicode.com/todos/1",
                headers: {
                    accept: "application/json",
                    "Content-Type": "application/json",
                }
            });
            console.log("SUCCESS!", JSON.stringify(result.data, null, 2));
        } catch(error) {
            console.log("FAIL!", error.message);
        }
    };
    return <button onClick = {() => fetchLongRequest()}>Fetch </button> ;
}
export default App;

下面的原始解决方案完全是矫枉过正!

我认为这会做你想要的,使用 Promise.race

注意:就错误处理而言,这仍然不太正确

handleError功能纯粹是因此如果请求在超时之前失败,则不会输出两次失败

import React from "react";
import axios from "axios";

function App() {
    const fetchLongRequest = async () => {
        
        const waitTime = 5000;
        const handleError = error => {
            // this makes sure that the FAIL output isn't repeated in the case when there's a failure before the timeout
            if (!error.handled) {
                if (error.timedout) {
                    console.log("TIMEDOUT", error.timedout);
                } else {
                    console.log("FAIL!", error.message);
                    error.handled = true;
                    throw error;
                }
            }
        };
        const makeRequest = async () => {
            try {
                const result = await axios({
                    url: "https://jsonplaceholder.typicode.com/todos/1",
                    headers: {
                        accept: "application/json",
                        "Content-Type": "application/json",
                    }
                });
                console.log("SUCCESS!", JSON.stringify(result.data, null, 2));
            } catch(error) {
                return handleError(error);
            }
        };
        const timer = new Promise((_, reject) => setTimeout(reject, waitTime, {timedout: "request taking a long time"}));
        try {
            await Promise.race([makeRequest(), timer]);
        } catch(error) {
            handleError(error);
        }
    };
    return <button onClick = {() => fetchLongRequest()}>Fetch </button> ;
}
export default App;

async附带说明一下,如果没有/ ,这段代码要干净得多await——不过,公平地说,我使用async/await不像我单独使用 Promises 那样流利——我在使用 Promises 之前就已经使用了.catch:p

async/await实施

import React from "react";
import axios from "axios";

function App() {
    const fetchLongRequest = () => {
        
        const waitTime = 5000;
        const handleError = error => {
            // this makes sure that the FAIL output isn't repeated in the case when there's a failure before the timeout
            if (!error.handled) {
                if (error.timedout) {
                    console.log("TIMEDOUT", error.timedout);
                } else {
                    console.log("FAIL!", error.message);
                    error.handled = true;
                    throw error;
                }
            }
        };
        
        const myRequest = axios({
            url: "https://jsonplaceholder.typicode.com/todos/1",
            headers: {
                accept: "application/json",
                "Content-Type": "application/json",
            }
        }).then(result => {
            console.log("SUCCESS!", JSON.stringify(result.data, null, 2));
        }).catch(handleError);
        
        const timer = new Promise((_, reject) => setTimeout(reject, waitTime, {timedout: "request taking a long time"}));
        
        return Promise.race([myRequest, timer]).catch(handleError);
    };
    return <button onClick = {() => fetchLongRequest()}>Fetch </button> ;
}
export default App;

当然“更清洁”只是我的意见


推荐阅读