1

Soy nuevo en JavaScript y estoy tratando de exportar un objeto con algunos datos que recojo de un archivo JSON.

El problema es que los datos del objeto son correctos justo antes de salir de la función, dentro del fetch, pero al salir el arreglo de objetos se vacía.

const setEnumContent = () => {
    let enumContent = [];
    let chapterNumber = 0;
    
    fetch(SRC_REFS_DATA_CONTENTS)
        .then(response => response.json())
        .then(data => {
            data.forEach(element => {
                if (element.type == "chapter") {
                    let content = {};

                    content.reference = element.reference;
                    content.chapter = (++chapterNumber);
                    
                    enumContent.push(content);
                }                
            });
        });

    return enumContent;
}

Si imprimo el tamaño del objeto retorno me devuelve el valor 0, es decir:

console.log(setEnumContent().length);

Pero si realizo una impresión dentro de la función de la promesa sí funciona correctamente.

OscarGarcia
  • 26,999
  • 3
  • 26
  • 61
  • Tienes un problema con las peticiones asíncronas. Cuando se ejecuta `return` el contenido de `enumContent` aún no ha sido obtenido. ¿Cómo llamas a esta función? La solución está en cambiar la manera en la que llamas a la función para permitir que trates los datos cuando éstos hayan sido obtenidos, y no solo modificando tu función. – OscarGarcia Jan 19 '22 at 07:23
  • Que tendría que añadir para hacer que el retorno solo se de cuando los datos se obtengan?, la función se llama para crear un listado que permita comparar en que capitulo se encuentra. – Gerson Benavides Jan 19 '22 at 07:34
  • Estoy redactando una respuesta. Es compleja, así que voy a tardar unos minutos más. – OscarGarcia Jan 19 '22 at 07:35
  • Gracias héroe sin capa :D – Gerson Benavides Jan 19 '22 at 07:36
  • He terminado de redactar la respuesta con las dos soluciones que puedes usar. Espero que te sea de ayuda :) – OscarGarcia Jan 19 '22 at 07:56
  • ¿Qué tal te fue? Si no entiendes alg o tienes alguna pregunta no dudes en plantearla en los comentarios. – OscarGarcia Jan 19 '22 at 09:06
  • Tuve que replantearme la forma en que se llamaría la función y entrar a estudiar un poco el tema de la función de retrollamada pero se logró, muchas gracias. – Gerson Benavides Jan 19 '22 at 10:18
  • Me alegra ser de ayuda. Recuerda marcar la respuesta como correcta pulsando en el icono **✓** que hay junto a ella. – OscarGarcia Jan 19 '22 at 11:22

1 Answers1

1

Cuando invocas una función asíncrona, ésta no se resuelve hasta que ha completado su trabajo (por ejemplo, la petición al servidor que estás solicitando).

Eso significa que el código que va tras el fetch, el return enumContent, se ejecuta ANTES que las funciones definidas dentro de los then().

Tienes varias formas de resolver el problema. La más sencilla es proporcionando una función de retrollamada como parámetro, pero la más elegante es haciendo uso de funciones asíncronas.

Te pongo un ejemplo de cada una de ellas y una descripción superficial de cada solución.

Función de retrollamada

Una retrollamada es un método de comunicación entre procesos por el cual proporcionamos como parámetro una función que puede ser llamada una o varias veces cuando se produzca un evento (por ejemplo, al finalizar un trabajo).

const SRC_REFS_DATA_CONTENTS =
  "https://api.github.com/users/ojgarciab/events/public";

const setEnumContent = (retrollamada) => {
  let enumContent = [];
  let chapterNumber = 0;
  fetch(SRC_REFS_DATA_CONTENTS)
  .then(response => response.json())
  .then(data => {
    data.forEach(element => {
      if (element.type == "PushEvent") {
        enumContent.push({
          reference: element.repo.name,
          chapter: ++chapterNumber,
        });
      }
    });
    /* Aquí hacemos la retrollamada con los datos */
    retrollamada(enumContent);
  });
}

/* Ahora llamamos a la función proporcionando qué hacer en la retrollamada */
setEnumContent(datos => console.log(datos));

Función asíncrona

Una función asíncrona es una función que puede ejecutar en su interior otras funciones asíncronas como si se ejecutaran de manera secuencial (usando await), ejecutándose cada tarea después de que la anterior haya finalizado.

const SRC_REFS_DATA_CONTENTS =
  "https://api.github.com/users/ojgarciab/events/public";

/* Definimos la función como asíncrona */
const setEnumContent = async () => {
  let enumContent = [];
  let chapterNumber = 0;
  /* Obtenemos la respuesta y los datos */
  const respuesta = await fetch(SRC_REFS_DATA_CONTENTS);
  const data = await respuesta.json();
  /* Procesamos los datos obtenidos */
  data.forEach(element => {
    if (element.type == "PushEvent") {
      enumContent.push({
        reference: element.repo.name,
        chapter: ++chapterNumber,
      });
    }
  });
  /* Devolvemos los valores */
  return enumContent;
}

/* Ahora llamamos a la función tratándola como una promesa */
setEnumContent().then(datos => console.log(datos));
OscarGarcia
  • 26,999
  • 3
  • 26
  • 61