1

I've looked but haven't been able to find a solution to this specific problem, so I thought I'd ask. I'm a novice javascript developer who clearly needs to read more about scope, callbacks and promises.

I'm trying to nest callbacks to get data out of a http request using the fetch API in javascript. At this point in my project, I've sent data to a node back end, called a few apis, then sent json data back to the client.

I now want to access that data outside of the function getServerData in the below.

I've tried a few different things but haven't been able to figure it out. I feel like I'm missing something obvious.

My current code is below:

//I want to access callback data here

    const getServerData = userData => {
      // declare data to send
      const apiData = userData;
    
      // declare route
      const url = 'http://localhost:3030/nodeserver';
    
      //   declare POST request options
      const options = {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(apiData)
      };
    
      // Async function to fetch from Node server w/ callback
      const fetchUIData = async callback => {
        await fetch(url, options)
          .then(response => response.json())
          .then(data => {
            callback(data);
          })
          .catch(err =>
            console.log(
              `There was an error fetching from the API POST route:${err}`
            )
          );
      };
    
      // Variable to store callback data
      let serverData = [];
    
      // Callback function to use data from fetch
      return fetchUIData(data => {
        serverData = data;
        console.log(serverData);
      });
    };
t.niese
  • 39,256
  • 9
  • 74
  • 101

2 Answers2

1

You don't nest callbacks when using await. The whole point of await is to get rid of the .then( .then( .then())) callback hell and nesting. Either use .then() (if you enjoy callback hells :) or await; not both together, it doesn't make sense.

const getServerData = async userData => {
    const url = 'http://localhost:3030/nodeserver';

    const options = {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(userData)
    };

    const response = await fetch(url, options);
    return response.json()
};

const serverData = await getServerData(userData);
console.log(serverData); // <-- Tadaaa
Jeremy Thille
  • 26,047
  • 12
  • 43
  • 63
0

As soon as the result is based on some asynchronous work, you need to return it using a Promise or a callback.

So const getServerData = userData => { has to either accept a callback, or to return a Promise.

The callback in that part of your code does not seem to have any purpose:

return fetchUIData(data => {
 serverData = data;
  console.log(serverData);
});

It seems as if you just want to return data from getServerData

So also that part does not make any sense:

.then(data => {
  callback(data);
})

So the code could look like this:

const getServerData = userData => {
  // declare data to send
  const apiData = userData;

  // declare route
  const url = 'http://localhost:3030/nodeserver';

  //   declare POST request options
  const options = {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(apiData)
  };

  // Async function to fetch from Node server w/ callback
  const fetchUIData = () => {
    return fetch(url, options)
      .then(response => response.json())
      .catch(err =>
        console.log(
          `There was an error fetching from the API POST route:${err}`
        )

        return null;
      );
  };

  // Callback function to use data from fetch
  return fetchUIData()
};

But even that could be simplified:

const getServerData = async userData => {
  // declare data to send
  const apiData = userData;

  // declare route
  const url = 'http://localhost:3030/nodeserver';

  //   declare POST request options
  const options = {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(apiData)
  };

  // fetch from Node server
  try {
    let response = await fetch(url, options)
    return response.json()
  } catch( err )  {
    console.log(
        `There was an error fetching from the API POST route:${err}`
    )

    return null;
  }
};

The whole try-catch block is probably also something you want to remove, letting the error propagate. In the current form, the getServerData will always fulfill and only log the error. That's in many cases not what you want. But that depends on the exact use case.

The function is then used that way:

let data = await getServerData();
t.niese
  • 39,256
  • 9
  • 74
  • 101
  • In the `catch` block/method, you log the error but returning `null` will [implicitly convert promise rejection into promise fulfilment](https://blog.yousafkhan.me/common-promise-anti-patterns#converting-promise-rejection-in-to-fulfilment); you probably meant to throw the error. IMHO you should remove the `catch` method/block from the `getServerData` function and leave the responsibility of handling errors on the calling code. – Yousaf May 21 '21 at 06:37
  • @Yousaf I agree with that. That's why I wrote `The whole try-catch block is probably also something you want to remove, letting the error propagate. But that depends on the exact use case.` Returning null instead of an error could be a valid design choice, you could remove the try-catch block letting the original error propagate, or keep the try-catch and rethrow a different error. Without knowing the exact use case it is hard to judge which approach is the best. – t.niese May 21 '21 at 06:54
  • i agree. Anyways, i thought it was important to mention (for OP and others that might not be aware of this) that returning an error instead of re-throwing it will fulfil the promise. – Yousaf May 21 '21 at 06:56
  • @Yousaf That's true. – t.niese May 21 '21 at 06:57
  • Thanks very much - this is a comprehensive and super helpful answer. Lol at my various mistakes. Cheers - really appreciate it. – JimmyAllDay May 22 '21 at 00:18