Include a new key into an array of objects within an asynchronous function

I am struggling with my async/await setup in an API route where I need to merge data from two sources into one object before returning it. The problem arises when I try to push data into a second array within the .then() block, as the array named clone ends up empty ([]). How can I effectively handle making an api request, merging data, and returning the result?

The code snippet for fetching:

export default async function getProduct(product_id) {
  const product = await fetch(
    `${process.env.PRIVATE_APP_URL}/products/${product_id}.json`,
    {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
      },
    }
  ).then((result) => {
    return result.json();
  });
    return product.product;
}

Snippet from the API handler:

const recharge_subscription_res = await rechargeAPI(
  "GET",
  `https://api.rechargeapps.com/subscriptions?customer_id=${recharge_customer.id}`
);

const closest_array = recharge_subscription_res.subscriptions.filter(
  (e) => e.next_charge_scheduled_at == closest_date
);

let clone = [];

closest_array.forEach((element) => {
  getProduct(element.shopify_product_id).then((product) => {
    element.shopify_product_handle = product.handle;
    element.shopify_product_image_url = product.image.src;
    clone.push(element);
  });
});

console.log(clone);

The issue is that clone should log an array of objects like closest_array, but instead logs as an empty array. My situation is unique in that it involves an Express.js API rather than just front end tasks. Any help or insights would be greatly appreciated.

Answer №1

The original promise specification utilized the .then() method, while the new syntax conceals then's with the await keyword. When it comes to style preferences, it is advisable to stick to one particular style.

Regardless of the chosen style, there is a slight challenge when it comes to generating multiple promises within a loop. JavaScript iteration functions (such as map and forEach) handle synchronous functions. The common approach involves creating a collection of promises in a synchronous loop and then executing them concurrently using Promise.all(). Considering both aspects...

You have the option (although not mandatory) to revise your network request in this manner...

// considering we've used "async", let's incorporate await...
export default async function getProduct(product_id) {
  const url = `${process.env.PRIVATE_APP_URL}/products/${product_id}.json`;
  const options = { method: "GET", headers: { "Content-Type": "application/json" }};
  const result = await fetch(url, options);
  const product = await result.json();
  return product.product;
}

The usage of await is restricted at the top-level scope; it can only be utilized within an async function. Here, I will create a function name and hypothesize about the parameter

async function rechargeAndLookupProduct(recharge_customer) {
  const base = 'https://api.rechargeapps.com/subscriptions';
  const query = `customer_id=${recharge_customer.id}`;
  const recharge_subscription_res = await rechargeAPI("GET",`${base}?${query}`);

  const closest_array = recharge_subscription_res.subscriptions.filter(el =>
    el.next_charge_scheduled_at == closest_date
  );

  // crucial step: gathering promises synchronously
  // execute together via Promise.all()
  const promises = closest_array.map(element => {
    return getProduct(element.shopify_product_id)
  });
  const allProducts = await Promise.all(promises);
  // allProducts will hold an array of objects that are results from resolved promises
  const clones = allProducts.map((prod, index) => {
    // utilizing Object.assign for true "cloning"
    let closest = Object.assign({}, closest_array[index]);
    closest.shopify_product_handle = prod.handle;
    closest.shopify_product_image_url = prod.image.src;
    return closest;
  });
  // assuming no typos were made (highly likely), 
  // clones should contain the expected outcome
  console.log(clones);
}

Answer №2

Your current code has a critical flaw in the highlighted section. You have a dangling promise that needs to be awaited or returned. When you check the clone variable, none of the async operations with getProduct have finished, and no elements have been added yet.

let clone = [];

closest_array.forEach((element) => {
  getProduct(element.shopify_product_id).then((product) => {
    element.shopify_product_handle = product.handle;
    element.shopify_product_image_url = product.image.src;
    clone.push(element);
  }); // FLAW: dangling .then
});
console.log(clone); // FLAW: clone is not populated yet.

A better approach would be:

let clone = await Promise.all(closest_array.map((element) =>
  getProduct(element.shopify_product_id).then((product) => {
    element.shopify_product_handle = product.handle;
    element.shopify_product_image_url = product.image.src;
    return element;
  })
));
console.log(clone);

Although modifying element this way may not be ideal, using this method ensures all getProduct calls are processed simultaneously for improved efficiency. Promise.all handles awaiting all promises and collating their results into an array for easy retrieval as a single promise in an async function.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The bespoke node package does not have an available export titled

No matter what I do, nothing seems to be effective. I have successfully developed and launched the following module: Index.ts : import ContentIOService from "./IOServices/ContentIOService"; export = { ContentIOService: ContentIOService, } ...

WordPress display issue: AMCharts chart won't appear

I recently crafted an XY 2 series chart using the Amcharts library and have successfully integrated it into my WordPress website with the necessary plugin. This particular chart showcases load span values for construction spreader beams, and it was built ...

AJAX - Achieving Dual Outcomes with Page Reload

Recently, I've been working on code that pulls information from an XML feed using AJAX and displays it based on certain conditions: $.ajax({ type: "GET", url: "testxml.xml", dataType: "xml", success: function(xml) { $(xml).fin ...

How to Ensure Screen Opens in Landscape Mode with Phaser3

https://i.stack.imgur.com/keCil.pngIn my Phaser3 game, I am trying to achieve the functionality where the game opens horizontally in a webview without requiring the user to rotate their phone. The vertical photo below shows the game in its current state. W ...

What could be causing WidgEditor, the JavaScript text editor, to fail to submit any values?

After clicking submit and attempting to retrieve text from the textarea, I am encountering a problem where the text appears blank. The reason for this issue eludes me. function textSubmit() { var text = $("#noise").val(); console.log(text); consol ...

Allowing access from different domains when using Angular.js $http

Whenever I encounter a CORS issue while developing a webapp, my go-to solution is to brew some coffee. However, after struggling with it for some time, I am unable to resolve the problem this time and need assistance. Below is the client-side code snippet ...

What is the correct way to integrate the Ant Design library with Next.js for seamless server-side rendering?

I duplicated the official Next.js example using Ant Design at this link: https://github.com/vercel/next.js/tree/canary/examples/with-ant-design After cloning, I proceeded with npm install to install all dependencies. Then, I ran npm run dev to check if ev ...

Show a jQuery box when hovering and disappear when the mouse leaves

I'm currently working on creating a box that pops out when a link is hovered over. Although I have the basics covered, I've been struggling to fully achieve my desired outcome. You can view my progress here: http://jsfiddle.net/EpV87/1/ (please ...

I am encountering an issue where the Formdata is transmitting a null value in the req.body

I'm encountering an issue where the uploaded file sent from the client to the server using FormData is arriving with an empty body. Below is the code, can someone offer assistance? Client-side code - https://i.stack.imgur.com/mNAdM.png Server-side ...

Adjusting the sound levels of an HTML audio element using JavaScript

I'm looking to adjust the volume of an <audio> element when a user clicks on an image. HTML: <div id="cloud"> <img name="jsVolumeButton" src="images/cloud.png" width = "140px"/> </div> <audio id="player" src="sounds/rain. ...

The operation malfunctions if the variable input is below 50, causing it to produce inaccurate results

The function "calcFinalTotal()" was created to calculate the post-tax discount on a purchase of items that were previously totaled and stored in an input tag with the ID of "totaltaxamount". This function applies a 10% discount to orders between $50 to $ ...

Tips for concealing passwords and email addresses in URLs within an Express application

http://localhost:3000/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e297918790dd878f838b8edf838081a2858f838b8ecc818d8f">[email protected]</a>&pass=abc123 what is the best way to conceal email addresses and ...

Issue with Elastic Beanstalk not displaying ExpressJS HTTPS certificate

I am facing a challenge with my elastic beanstalk environment where my certificate from the certificate manager is being recognized by my EB environment name and triggering an error, which is expected since the certificate is for my domain and not elasticb ...

Generate a new perspective by incorporating two distinct arrays

I have two arrays containing class information. The first array includes classId and className: classes = [ {classid : 1 , classname:"class1"},{classid : 2 , classname:"class2"},{classid : 3 , classname:"class3"}] The secon ...

Using Next.js with Firebase emulators

I've been struggling to configure Firebase's V9 emulators with Next.js, but I keep running into the same error message. See it here: https://i.stack.imgur.com/Uhq0A.png The current version of Firebase I'm using is 9.1.1. This is how my Fir ...

Issue with Mongoose: The function within the .find method is not returning the expected result

How can I successfully pass the result of a function into the .find method? I am trying to use the argument passed via body-parser as the value for the key 'name' if the user submits a non-empty string. If the user submits an empty string, then a ...

MinifyJS - optimize all .js files within a directory

Seeking assistance to finalize my build process: I am currently transpiling es6 > es5 using babel. Once that is complete, I aim to minify all my .js files recursively with uglifyJS solely using NPM scripts (no grunt or gulp). My Requirements; Convert ...

The JavaScript-generated form element will not be included in the data submitted through the POST method

While it may appear that this question has been asked before, my specific inquiry seems to be unique as I have not found a similar answer in other threads. Therefore, I am initiating a new discussion. My issue revolves around a form which contains a link ...

Issue: Cannot assign type 'Promise<PostInfo>[]' to type 'PostInfo[]' while updating state

At the moment, I am facing an issue with my function that fetches an API and updates state. Specifically, I encounter an error when attempting to assign a Promise to the state. type DataState = { postList: Array<PostInfo>: }; const [state, setSt ...

React.js Form Validation issue: When using input type "number", the text field remains a single value even after attempting to delete characters with the backspace key

Experiencing difficulty in clearing the input field value, even after pressing backspace the value remains constant. One particular value persists in the input field. import * as React from "react"; import { Button, Form } from "react-boots ...