Search code examples
javascriptfetch-apinested-loops

Javascript: nested fetch with foreach loop..?


this is my problem: I need to make some fetch POST requests to my server. The server holds a list of names. Each name corresponds to a list of elements which I need to browse, in order to find a specific element from all the lists. I have to work with this structure, can't change it.

My approach was to write a fetch request, to get the all these names (of the lists I need to browse), then I would loop through each name and fetch to get the according lists from the server. Once I get these lists I can loop through the elements till I find the correct element.

This is not an easy task for me as I am new to javascript.

This is how I implemented this:

function browse(){
      fetch(TARGET_IP, {
            method: "POST",
            headers: {
                "Content-type": "application/json",
                "X-Auth-Token": AUTH_TOKEN
            },
            body: JSON.stringify({
                "id": messageId++,
                "jsonrpc": "2.0",
                "method": "Browse",
                "params": {
                    "mode": "children"
                }
            })
        })
            .then(response => response.json())
            .then((data) =>  {

                data.results = Object.assign(Array.prototype, data.result);

                data.results.forEach(element => {
                    if (element.datatype == "datablock") {

                        datablocks.push(element.name)
                    }
                })

                return datablocks;

            })
            .then((datablocks) => {
                datablocks.forEach(element => {
                    
                    browseDatablocks(element)
                })
            })

            .catch(e => console.error(e))
}


function browseDatablocks(dbname) {

    fetch(TARGET_IP, {
        method: "POST",
        headers: {
            "Content-type": "application/json",
            "X-Auth-Token": AUTH_TOKEN
        },
        body: JSON.stringify({
            "id": messageId++,
            "jsonrpc": "2.0",
            "method": "Browse",
            "params": {
                "var": "\"" + dbname + "\"",
                "mode": "children"
            }
        })
    })
        .then(response => response.json())
        .then((data) => {

            data.results = Object.assign(Array.prototype, data.result);

            data.results.forEach(element => {
                    
                if (element.name == "P_Index")
                {
                    console.warn(dbname);
                    // This is the element I need and would save it
                }
            })

        })

        .catch(e => console.error(e))
}


Now all sorts of strange things happen with this approach.

The console outputs elements that are just not the elements I search for, which is just wrong as I checked on server side.

Some requests are not even handled.

I assume these problems occur because I send too many requests at the same time (the nested ones in browseDatablocks). How can I handle one request and then the next one? Or are there other ways to implement this?

Thanks.


Solution

  • looks like what you need is Promise.all.

    if i understand the problem correctly:

    first return the fetch result in function browseDatablocks(dbname)

    ...
    function browseDatablocks(dbname) {
    
      return fetch(TARGET_IP, {
      ....
    

    And return the value, in last then

    .then((data) => {
      data.results = Object.assign(Array.prototype, data.result);
    
      const filtered = data.results.filter((element) => element.name == "P_Index");
    
      return filtered;
    });
    

    Then in function browse(), last then:

    .then((datablocks) => {
      const results = datablocks.map(element => browseDatablocks(element));
    
      return Promise.all(results);
    })
    .then((listOfResults) => {
      // Some magic here
    })
    

    // Edit or to do them in order:

    In function browseDatablocks(dbname), last then:

    do the handling, and return a bool instead of filtered eg true = all done, false fetch the next. Something like:

    .then((data) => {
      data.results = Object.assign(Array.prototype, data.result);
    
      const found = data.results.find(element => element.name == "P_Index");
      if (found) {
        // Some handling
        return true;
      }
      return false;
    })
    

    And in function browse(), last then

    .then(async (datablocks) => {
      let i = 0;
      while(i < datablocks.length) {
        const handled = await browseDatablocks(datablocks[i]);
    
        if (handled === true) {
          break;
        }
    
        i += 1;
      }
    });