Soy muy nuevo en JavaScript y la programación asíncrona. Tengo un servidor.js que raspa los valores de un sitio web de búsqueda de empleo relacionado con la palabra clave dada. Y una clase JobDataFetcher para buscarlos.
JobDataFetcher Clase:

class JobDataFetcher {
    constructor() {}

    async getSingleJobPostingCount(keyword) {
        const response = await fetch(`http://localhost:3000/${keyword}`);
        const jobPostingCount = await response.json();
        return {
            keyword: keyword,
            count: jobPostingCount
        };
    }

    async getJobPostingCounts(keywords) {
        const jobPostings = [];
        let postingsProcessed = 0;

        keywords.forEach(keyword => {
            this.getSingleJobPostingCount(keyword)
                .then(jobPosting => {
                    jobPostings.push(jobPosting);
                    postingsProcessed++;
                    if (postingsProcessed === keywords.length) {
                        console.log(jobPostings);
                        return jobPostings;
                    }
                });
        });
    }
}

El console.log(jobPostings) Dentro de esta clase me da lo que quiero, que es algo así:

(3) [{…}, {…}, {…}]
    0: {keyword: "Angular", count: 266}
    1: {keyword: "React", count: 232}
    2: {keyword: "Vue", count: 98}
    length: 3
    __proto__: Array(0)

Pero cuando estoy usando el método getJobPostingCounts() en mi app.js, devuelve indefinido sin esperar a que los datos sean recuperados:

    dataFetcher.getJobPostingCounts(keywords)
        .then(jobPostings => {
            console.log(jobPostings);
            // chart.destroy();
            // chart = ui.createChart(cardBodyUI, jobPostings);
        })
        .catch(err => console.log(err));

Probablemente debería poner un await en algún lugar de la clase getJobPostingCounts(keywords), pero después de muchos intentos, simplemente no pude averiguar dónde.

0
Mertcan Seğmen 15 jul. 2019 a las 13:41

1 respuesta

La mejor respuesta

Cada llamada de devolución de llamada forEach se ejecuta de forma sincrónica, por lo que una vez que los acabados forEach, la matriz jobPostings no ha sido poblada.

Mapa de cada getSingleJobPostingCount Llame a una promesa en una matriz, y llame a Promise.all en la matriz, que se resolverá una vez que cada una de esas promesas en la matriz ha resuelto:

getJobPostingCounts(keywords) {
  return Promise.all(
    keywords.map(this.getSingleJobPostingCount)
  );
}

(No es necesario que getJobPostingCounts sea async, ya que no usa await)

Sin embargo, toda la clase parece innecesaria aquí, nunca estás usando las instancias, solo estás usando la clase para las funciones en su prototipo. Considere el uso de un objeto (o funciones independientes) en su lugar:

const dataFetcher = {
  async getSingleJobPostingCount(keyword) {
    const response = await fetch(`http://localhost:3000/${keyword}`);
    const jobPostingCount = await response.json();
    return {
      keyword: keyword,
      count: jobPostingCount
    };
  },
  getJobPostingCounts(keywords) {
    return Promise.all(
      keywords.map(this.getSingleJobPostingCount)
    );
  }
};
2
CertainPerformance 15 jul. 2019 a las 10:51