skip to Main Content

I need to update an image in a <canvas> 10 times per second, with data coming from an HTTP request:

var i = 0;
setInterval(() => {
    i += 1;
    console.log("sending request", i);
    fetch("/data")   // this can sometimes take more than 100 ms
        .then((r) => r.arrayBuffer())
        .then((arr) => {
            // update canvas with putImageData...
            console.log("finished processing", i);
        });

}, 100);

It works, but since the request sometimes takes more than 100ms, it gives results like:

sending request 1
sending request 2
sending request 3
finished processing 3
sending request 4
sending request 5
finished processing 5
sending request 6
sending request 7
sending request 8

Question:

Are the "not-finished-to-process" requests queued for later (then it will make everything even slower!), or are they just dropped? By the way, how can I modify my example in order to test if it’s the former or the latter (I haven’t been able to to a MCVE confirming the former or latter)?

Since it’s video, I need to drop frames if the server takes too long to respond, how would you do it?

PS: example Python server code that takes 150 ms to respond:

import bottle, time
app = bottle.Bottle()
@bottle.route('/')
def index():
    return open("test.html", "r").read()
@bottle.route('/data')
def data():
    time.sleep(0.150)
    return b"0000"
bottle.run(port=80)

2

Answers


  1. Your output is currently wrong, therefore I would extract the code in a function, so that you always know which i you are currently in.

    Take a look at the async keyword in JavaScript. This helps you to wait for a promise to resolve before making another request. (If you don’t want to wait just remove the await in the setInterval). The sendRequest function will always resolve after 100ms.

    var i = 0;
    setInterval(async () => {
        i += 1;
        console.log("sending request", i);
        await sendRequest(i) // you may want to remove await here
    }, 100);
    
    async function sendRequest(i) {
        setTimeout(resolve, 100); // resolves the promise if the api takes longer than 100ms
    
        const data = await fetch("/data");   // this can sometimes take more than 100 ms
        const arr = data.arrayBuffer();    
         // update canvas with putImageData...
    
        console.log("finished processing", i);
    }
    
    Login or Signup to reply.
  2. I think you can make use of the AbortControl API which will help you in taking the latest api response. AbortController cancels the other APIs if multiple API hits are there and in that case, you will always be receiving the latest API response. This strategy is useful when you are polling the server in regular intervals. FETCH and Axios both provide access to AbortControler. Below I’m add a custom hook which I used in my project. I hope it will help you in understanding the concept.

    It is built using Axios where I’m using ref as a reference to the previous call.

    import axios from "axios";
    import { useEffect, useRef, useState } from "react";
    
    const AbortApiController = (url, page) => {
    
    const cancelToken = useRef(null);
    const [data, setData] = useState([]);
    const [reason, setReason] = useState('');
    const [loading, setLoading] = useState(false);
    
    useEffect(() => {
        const abortContol = async () => {
            if (cancelToken.current) {
                const str = 'cancled due to new request!';
                cancelToken.current.cancel(str);
            }
            setLoading(true);
            cancelToken.current = axios.CancelToken.source();
            try {
                let response = await axios.get(url, {
                    cancelToken: cancelToken.current.token
                });
                setData(prev => [...prev, ...response.data]);
                setLoading(false);
                return response.data;
            } catch (error) {
                setReason(error.message);
                setLoading(false);
            }
        }
        abortContol();
    }, [page]);
    
    return { data, reason, loading };
    }
    
    export default AbortApiController;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search