1

I am working with react functional component.

I have download(async) functionality in my application. There is list of download buttons in my app. I want to download them one by one in queue.

const [downloadQueue, setDownloadQueue] = useState([]);

onClickDonwload = () => { //on every download click this function calls. So user can click on download continuously but I want to make sure them to be downloaded one by one.
  downloadQueue([...downloadQueue, { id, data }])
}

useEffect(() => {
  downloadQueue.map(async(data) => {
    await myDownloadAsyncFunction()
  })
}, [downloadQueue])  // here the issue is whenever the downloadQueue updates it just start downloading without waiting for the previous one

Kindly help

2
  • Does this answer your question? How do I cancel an HTTP fetch() request? Commented Sep 25, 2022 at 7:36
  • @AshishChoudary What does myDownloadAsyncFunction do - can you edit the question to include more detail on it? For example, include a simplified version of its code? Commented Sep 25, 2022 at 15:04

2 Answers 2

1

How about this? It uses Promise-chaining to make a simple queue

import React, { useState } from "react";

// Any async function or function that returns a promise
async function myDownloadAsyncFunction(data) {
  return fetch('https://mydomain.test/path');
}

function DownloadButton() {
  const [queue, setQueue] = useState(Promise.resolve());

  onClickDownload = () => {
    setQueue(queue
      .then(() => myDownloadAsyncFunction('My data'))
      .catch((err) => {console.error(err)})
    )
  }

  return (
    <button onClick={onClickDownload}>Download</button>
  );
}

The above doesn't trigger the download from useEffect. If you do want to use useEffect, I think the state would probably need to be a counter to cause useEffect to run when it changes:

import React, { useState, useEffect, useRef } from "react";

// Any async function or function that returns a promise
async function myDownloadAsyncFunction(data) {
  return fetch('https://mydomain.test/path');
}

function DownloadButton() {
  const queue = useRef(Promise.resolve());
  const [clickCount, setClickCount] = useState(0);

  useEffect(() => {
    if (clickCount == 0) return;
    queue.current = queue.current
      .then(() => myDownloadAsyncFunction('My data'))
      .catch((err) => {console.error(err)});
  }, [clickCount]);

  function onClickDownload() {
    setClickCount(clickCount + 1);
  }

  return (
    <button onClick={onClickDownload}>Download</button>
  );
}

Note in both of the above examples might need to get more complicated to better deal with some of the downloads failing. In these examples though, if a download fails, the next one should continue to attempt to be downloaded.

Sign up to request clarification or add additional context in comments.

15 Comments

If I want to pass data in myDownloadAsyncFunction I can do like this setDownloadQueue(downloadQueue.finally(myDownloadAsyncFunction(data))) right?
Hm, there's only one unhandled promise rejection at a time, but it's a new one with every step; firing an unhandledrejection event for the new promsie and a rejectionhandled event for the previous promise. How that is displayed in the devtools seems to differ.
Exactly, the queue = queue.then(runStep).catch(console.error) is what I recommend
Yes that's what I am saying... You have used 1000 in setInterval that is why it will run it in series but in my case that's api call so we do not know which download click will take how much time. Isn't it?
@AshishChoudary Ah the 1000 here is just to emulate a request takes 1000ms so the example works without talking to another server. In the real case, the myDownloadAsyncFunction function can be return fetch('https://domain.test/path'), and it won't depend on how long it takes.
|
0

A simple way is :

const [downloading,setDownloading] = useState(false)

useEffect( ()=> {
  if(downloadQueue.length && !downloading){
    myDownloadAsyncFunction(downloadQueue[0].data).then(()=>{
      setDownloading(false)
      setDownLoadQueue((a) => a.slice(1))
    })
  }
},[downloadQueue])

From your code, I'm not sure how do you pass parameter to myDownloadAsyncFunction(). Here I juse assume that it use downloadQueue[0].data.

3 Comments

This will download the same thing multiple times
@Bergi Do you have any solution? kindly suggest
@Bergi, Yes, you are right. I forgot to add a downloading state. Have updated my answer and it should works now

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.