每2秒发起一次Fetch请求,但不希望请求堆积。

4

我正在尝试进行API调用,并希望每2秒重复一次。但是,如果在2秒内系统没有收到请求的回复,它会积累请求并继续尝试发送它们,这让我有些担心。如何防止这种情况发生?

这是我要执行的fetch操作:

const getMachineAction = async () => {
    try {
        const response = await fetch( 'https://localhost:55620/api/machine/');
        if (response.status === 200) {
            console.log("Machine successfully found.");
            const myJson = await response.json(); //extract JSON from the http response
            console.log(myJson);               
        } else {
            console.log("not a 200");
        }
    } catch (err) {
        // catches errors both in fetch and response.json
        console.log(err);
    }
};

然后我使用setInterval函数调用它。

function ping() {
    setInterval(
        getMachineAction(),
        2000
    );        
}

我曾考虑在 setInterval 函数中添加一些 promise 结构来确保 fetch 已经成功并完成,但是无法使其正常工作。


1
每2秒一次是确切的要求吗?如果不是,请不要每2秒执行一次。使用setTimeout,在上一个完成后安排下一个2秒。 - zero298
将会有一个固定的时间,无论是2秒还是30秒将取决于一个变量,但它将保持一致。(所以如果设定为每2秒一次,则需要每2秒发生一次) - thalacker
1
也许可以查一下 throttling/debouncing - 我忘记哪个是哪个了。lodash 都有这两种方法。 - Andy
1
在2秒的时刻,你希望对之前的请求发生什么?它应该取消吗?你的“坏情况”要求是什么? - zero298
1
@thalacker takrishna的方法是正确的。此外,也可以参考这里 - Bergi
显示剩余3条评论
3个回答

8
Promise.all() 解决方案
该解决方案确保您不会错过 2 秒延迟要求,并且在另一个网络调用正在进行时也不会发起调用。

function callme(){
//This promise will resolve when the network call succeeds
//Feel free to make a REST fetch using promises and assign it to networkPromise
var networkPromise = fetch('https://jsonplaceholder.typicode.com/todos/1');


//This promise will resolve when 2 seconds have passed
var timeOutPromise = new Promise(function(resolve, reject) {
  // 2 Second delay
  setTimeout(resolve, 2000, 'Timeout Done');
});

Promise.all(
[networkPromise, timeOutPromise]).then(function(values) {
  console.log("Atleast 2 secs + TTL (Network/server)");
  //Repeat
  callme();
});
}
callme();

注意:这个处理方式满足了问题作者提出的“坏情况”定义:
“‘坏情况’(即执行时间超过2秒)是我希望它跳过该请求,然后发送一个新请求。因此,在0秒时发送请求。执行需要3秒,然后在2秒后(在5时)应重新执行。因此,它只是延长时间直到发送请求。”

看起来这样做非常好。callme()函数还是networkPromise将成为我的getMachineAction呢? - thalacker
1
networkPromise 将会调用 getMachineAction - callme 只是一个包装器!!祝你编程顺利。 - takrishna
我现在看懂了。呃,愚蠢的错误。谢谢! - thalacker

6
你可以在你的try/catch中添加finallysetTimeout而不是使用setInterval。请注意,像这样的长轮询比使用Websockets创建更多的服务器负载,而Websockets本身更加实时。
const getMachineAction = async () => {
    try {
        const response = await fetch( 'https://localhost:55620/api/machine/');
        if (response.status === 200) {
            console.log("Machine successfully found.");
            const myJson = await response.json(); //extract JSON from the http response
            console.log(myJson);               
        } else {
            console.log("not a 200");
        }
    } catch (err) {
        // catches errors both in fetch and response.json
        console.log(err);
    } finally {
        // do it again in 2 seconds
        setTimeout(getMachineAction , 2000);
    }
};

getMachineAction()

2
请注意,这将在请求完成后的2000秒内发生,而不是请求开始后的2000秒。 - wizzwizz4
@charlietfl,你有什么关于WebSockets的资源可以推荐阅读吗? - thalacker
你的后端是用什么语言编写的?只需要搜索<语言> websockets即可。如果是Node服务器,请查看socket.io。 - charlietfl
@charlietfl 我正在运行一个C#,.NET Core Web API。我可以搜索C#网络套接字。主要通过外部设备连接以收集数据。 - thalacker

0

很简单!只需存储当前是否正在进行请求,并存储计时器是否已触发而不发送新请求。

let in_progress = false;
let missed_request = false;
const getMachineAction = async () => {
    if (in_progress) {
        missed_request = true;
        return;
    }
    in_progress = true;
    try {
        const response = await fetch('https://localhost:55620/api/machine/');
        if (missed_request) {
            missed_request = false;
            setTimeout(getMachineAction, 0);
        }
        if (response.status === 200) {
            console.log("Machine successfully found.");
            const myJson = await response.json(); //extract JSON from the http response
            console.log(myJson);               
        } else {
            console.log("not a 200");
        }
    } catch (err) {
        // catches errors both in fetch and response.json
        console.log(err);
    } finally {
        in_progress = false;
    }
};

要开始间隔,您需要省略()

setInterval(getMachineAction, 2000); 

嗨@wizzwizz4,如果我可以跳过请求(即在2,4,8,10执行并跳过6),那么这可能有效,但我希望它能像尽快执行,然后再过2秒钟执行(即在2,4,7,9,10执行,其中6需要额外的1秒钟,所以现在是2秒钟之后)。此外,我认为你需要在“try”后的第一行将你的“in_progress”变量设置为true,对吧?感谢您抽出时间来回答! - thalacker
@thalacker 是的,我有。 (或者前面的第一行,任何一个都可以。)如果从4开始的那个需要2.3秒钟,而不像charlietfi的解决方案,这应该做2、4、6.3、8、10。Promise.all的解决方案很简洁,但似乎与chatlietfi的解决方案(2、4、6.3、8.3、10.3)几乎完全相同。您正在寻找哪种功能? - wizzwizz4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接