javascriptasync-awaitpromise

Problem with Promises and async/await in JavaScript


I'd like to consume data from a webservice and display it. The data has some dependencies on each other so that only one group of data can be displayed once all data of this group is read. All requests should start as fast as possible.

My code so far:

function asyncFetch(url = "", data = {}) {
  return fetch(url, {
      method: "POST",
      mode: "cors",
      cache: "no-cache",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data),
    })
    .then(response => (response.json()))
    .catch(console.error);
}

const fetchOPDebitoren = () => {
  console.log(" fetch fetchOPDebitoren");

  return asyncFetch("http://server:3000/readOP", {
    token: "mytoken",
    options: {
      vpkt: "1000",
      bpkt: "6999"
    }
  });
};

const fetchOPKreditoren = () => {
  console.log("fetch fetchOPKreditoren");

  return asyncFetch("http://server:3000/readOP", {
    token: "mytoken",
    options: {
      vpkt: "7000",
      bpkt: "9999"
    }
  });
};

const fetchSalesData = () => {
  console.log(" fetch SalesData");

  return asyncFetch("http://server:3000/sales", {
    token: "mytoken",
    options: {
      von_pkt: "10000",
      bis_pkt: "99999"
    }
  });
};

const displayDueBarDashboard = () => {
  Promise.all([fetchOPDeb(), fetchOPKred()])
    .then(([op_data_deb, op_data_kred]) => {
      console.log(" fetch OPs completed");

      return new Promise((resolve) => {
        /* display the data */
        console.log("display op data");
      });
    })
    .catch((error) => console.error('Error display DueBarChart:', error))
};

const displaySalesChart = () => {
  Promise.all([fetchSalesData()])
    .then(([sales_data]) => {
      console.log("fetch SalesChart completed");

      return new Promise((resolve) => {
        /* display the data */
        console.log("display sales data");

      });
    })
    .catch((error) => console.error('Error display SalesChart:', error));
};


await Promise.all([
    displayDueBarDashboard(),
    displaySalesChart()
  ])
  .then(() => {
    console.log("All done, cleanup");
  });

As you can see, to display the DueBars the data of both fetchOP functions is needed.

With this code, the problem is now that the cleanup is called before all fetches are done and the display output is never shown.

fetch fetchOPDebitoren index.html:442:13
fetch fetchOPKreditoren index.html:465:13
fetch SalesData index.html:488:13
All done, cleanup index.html:563:13
fetch OPs completed index.html:539:14
fetch SalesChart completed

What am I doing wrong?

Also, in the fetch functions I must present a token. This token is requested with another call of asyncFetch which I have to wait for.

How can I implement that in the fetch functions without making them async?


Solution

  • Promise chains not waiting for completion due to unresolved promises

    The issue in your code is that your display functions don't properly resolve their promises, causing the main Promise.all() to complete prematurely.

    The problem

    Both displayDueBarDashboard() and displaySalesChart() create promises that never resolve. When you return a new Promise without calling resolve(), it stays pending forever, but the functions themselves still return immediately.

    Additionally, you're referring to fetchOPDeb() and fetchOPKred() instead of your defined functions fetchOPDebitoren() and fetchOPKreditoren().

    Solution

    const displayDueBarDashboard = () => {
      return Promise.all([fetchOPDebitoren(), fetchOPKreditoren()])
        .then(([op_data_deb, op_data_kred]) => {
          console.log("fetch OPs completed");
          
          /* display the data */
          console.log("display op data");
          
          // Return something or the promise will just pass through the results
          return "DueBar completed";
        })
        .catch((error) => console.error('Error display DueBarChart:', error));
    };
    
    const displaySalesChart = () => {
      return Promise.all([fetchSalesData()])
        .then(([sales_data]) => {
          console.log("fetch SalesChart completed");
          
          /* display the data */
          console.log("display sales data");
          
          // Return something to resolve the promise
          return "Sales completed";
        })
        .catch((error) => console.error('Error display SalesChart:', error));
    };
    

    For the token requirement

    To handle token acquisition first:

    const getToken = () => {
      return asyncFetch("http://server:3000/getToken", {
        // token request parameters
      }).then(response => response.token);
    };
    
    const fetchWithToken = (url, options) => {
      return getToken().then(token => {
        return asyncFetch(url, {
          ...options,
          token: token
        });
      });
    };
    
    const fetchOPDebitoren = () => {
      console.log("fetch fetchOPDebitoren");
      return fetchWithToken("http://server:3000/readOP", {
        options: {
          vpkt: "1000",
          bpkt: "6999"
        }
      });
    };
    

    Complete solution

    Here's how you could restructure everything:

    // Token acquisition
    const getToken = () => {
      return asyncFetch("http://server:3000/getToken", {})
        .then(response => response.token);
    };
    
    // Cache token to avoid multiple requests
    let tokenPromise = null;
    const getTokenOnce = () => {
      if (!tokenPromise) {
        tokenPromise = getToken();
      }
      return tokenPromise;
    };
    
    // Data fetching functions
    const fetchOPDebitoren = () => {
      console.log("fetch fetchOPDebitoren");
      return getTokenOnce().then(token => {
        return asyncFetch("http://server:3000/readOP", {
          token: token,
          options: {
            vpkt: "1000",
            bpkt: "6999"
          }
        });
      });
    };
    
    // Repeat same pattern for other fetch functions
    
    // Display functions
    const displayDueBarDashboard = () => {
      return Promise.all([fetchOPDebitoren(), fetchOPKreditoren()])
        .then(([op_data_deb, op_data_kred]) => {
          console.log("fetch OPs completed");
          console.log("display op data");
          return "DueBar completed";
        });
    };
    
    const displaySalesChart = () => {
      return Promise.all([fetchSalesData()])
        .then(([sales_data]) => {
          console.log("fetch SalesChart completed");
          console.log("display sales data");
          return "Sales completed";
        });
    };
    
    // Main execution
    Promise.all([
      displayDueBarDashboard(),
      displaySalesChart()
    ])
    .then((results) => {
      console.log("All done, cleanup", results);
    })
    .catch(error => {
      console.error("Error in main process:", error);
    });
    

    This approach ensures all promises resolve properly and handles token acquisition efficiently.