L oop через массив для генерации нового массива. Возвращает 0 в конце цикла - PullRequest
0 голосов
/ 03 мая 2020

В базе данных 4900 книг. Я ожидаю иметь такое же количество книг в новом массиве в конце l oop. Тем не менее, я получаю нулевую длину для booksArray. В чем может быть проблема и возможное решение?

const getOverview = async(req, res) => {

    const books = await Book.find();

    const booksArray = new Array();

    books.forEach(book => {

        const url =`https://www.goodreads.com/book/isbn/${book.isbn}key=${process.env.KEY}`;

        request.get(url).then(result => {

            parseString(result, (error, goodReadsResult) => {

                const goodreadsBook = goodReadsResult.GoodreadsResponse.book[0];

                booksArray.push(goodreadsBook);

            })
        });
    })
    console.log(booksArray.length);
};

1 Ответ

0 голосов
/ 04 мая 2020

Сначала немного обзора. Node.js делает все сети неблокирующими и асинхронными. Это означает, что когда вы делаете сетевой запрос, он запускает операцию, немедленно возвращается и продолжает выполнять остальную часть вашего кода. Обратный вызов или обещание, связанное с этой асинхронной операцией, вызываются через некоторое время.

В вашем конкретном случае c весь ваш .forEach() l oop выполняется до завершения, запуская все сетевые запросы в нем до того, как они закончили. Таким образом, вы выполняете console.log(booksArray.length); до запуска любого из ваших обратных вызовов, и, следовательно, массив все еще пуст.

Каждый раз, когда вы хотите упорядочить или координировать более одной асинхронной операции в node.js, вы захотите использовать обещания. Он был встроен в язык уже пару лет, и это просто намного лучший способ программирования с асинхронными операциями. Я бы посоветовал вам найти несколько хороших руководств по работе с обещаниями и изучить их.

Затем используемая вами библиотека request() устарела, переведена в режим обслуживания и не поддерживает обещания. Существует множество более современных альтернатив . Я лично использую библиотеку got() , и это то, что я проиллюстрировал ниже.

Тогда я не знаю, какова ваша функция parseString(), но она, очевидно, не использовать обещания и кажется асинхронным, поэтому, чтобы использовать его в рабочем процессе на основе обещаний, я «обещал» его, используя util.promisify(), который встроен в node.js. Если у него есть интерфейс с обещаниями, вы должны просто использовать его напрямую.

Так или иначе, вот что я бы предложил:

const got = require('got');
const {promisify} = require('util');
const ps = promisify(parseString);

// this function returns a promise  
async function getOverview(req, res) {

    const books = await Book.find();
    const booksArray = await Promise.all(books.map(book => {
        const url =`https://www.goodreads.com/book/isbn/${book.isbn}key=${process.env.KEY}`;
        let bookData = await got(url);
        return ps(bookData);
    }));

    console.log(booksArray.length);

    // make the booksArray be the resolved value of the promise returned
    // by this async function
    return booksArray;
}

Это делает все ваши сетевые вызовы параллельно, а затем использует Promise.all(), чтобы указать, когда они все сделаны, и собрать все результаты по порядку.

Если по какой-то причине вы не можете выполнять все эти запросы параллельно (например, их так много, что объекты целевого сервера), то вы можете запускать их по одному тоже так:

const got = require('got');
const {promisify} = require('util');
const ps = promisify(parseString);

// this function returns a promise
async function getOverview(req, res) {

    const books = await Book.find();
    const booksArray = [];
    for (let book of books) {
        const url =`https://www.goodreads.com/book/isbn/${book.isbn}key=${process.env.KEY}`;
        let bookData = await got(url);
        let data = await ps(bookData);
        booksArray.push(data);
    }

    console.log(booksArray.length);

    // make the booksArray be the resolved value of the promise returned
    // by this async function
    return booksArray;
}

И, поскольку это async функции, они возвращают обещание, поэтому вызывающий должен использовать await или .then() для получения значения из обещания.

getOverview(...).then(results => {
    console.log(results);
}).catch(err => {
    console.log(err);
});

Если вы действительно намереваетесь передать req и res в эту функцию и использовать их там, то вам, вероятно, потребуется локальная обработка ошибок, поэтому, если какая-либо из ваших операций await отклоняется, вы можете перехватить эту ошибку и отправить ошибку ответ. Вы сделали бы это с try/catch внутри тела функции. Вероятно, вы можете просто использовать один верхний уровень try/catch и затем отправить ответ об ошибке в обработчике catch.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...