Refractroing: вернуть или переместить значение в новое значение массива из обратного вызова mongoose

1

На самом деле я не уверен, что Название моего вопроса "правильно", если у вас есть идея с ним, вы можете оставить комментарий, и я его переименую.

Я пытаюсь переписать мою старую функцию, которая делает http-запросы и вставляет много объектов в mongoDB через mongoose. У меня уже есть рабочая версия, но я сталкиваюсь с проблемой при ее использовании. В основном, потому что, когда я пытаюсь вставить 20 массивов из запроса 20+ с ~ 50'000 элементами из одного запроса, это вызывает огромную утечку памяти. Даже с оптимизацией MongoDB.

Логика моего кода:

function main() {
    server.find({locale: "en_GB"}).exec(function (err, server) {
        for (let i = 0; i < server.length; i++) { //for example 20 servers
            rp({url: server[i].slug}).then(response => {
                auctions.count({
                    server: server[i].name,
                    lastModified: {$gte: response.data.files[0].lastModified}
                }).then(function (docs) {
                    if (docs < 0) {
                      //We don't insert data if they are already up-to-date
                    }
                    else {
                        //I needed response.data.files[0].url and server[i].name from prev. block
                        //And here is my problem
                        requests & insertMany and then => loop main()
                        })
                    }
                })
            }).catch(function (error) {
                console.log(error);
            })
        }
    })
}

main()

На самом деле, я уже много раз пытался это исправить. Во- первых, из-все, что я пытался добавить setInterval после else блока, как это:

setTimeout(function () {
    //request every server with interval, instead of all at once
}, 1000 * (i + 1));

но я создаю еще одну проблему для себя, потому что мне нужно было рекурсивно выполнить функцию main() сразу после. Поэтому я не могу использовать: if (i === server[i].length-1) чтобы вызвать сборщик мусора или перезапустить main() потому что не все проверки count пропусков сервера

Или посмотрим другой пример:

Я изменяю for (let я = 0; я < server.length; i++) от 3-й строки до .map и перемещает ее из 3-й строки близко к else но setTimeout не работает с .map версией, но, как вы уже можете понять, скрипт теряет правильный порядок, и я не могу сделать с ним задержку.

На самом деле я уже понимаю, как это исправить сразу. Просто заново создайте массив с помощью let array_new = [], array_new.push = response.data.files[0].url с использованием async/await. Но я не большой эксперт в этом, поэтому я уже теряю пару часов. Так что единственная проблема на данный момент, что я не знаю, как return значения из else block

На данный момент я пытаюсь сформировать массив внутри блока else

function main() {
--added let array_new = [];

[v1]array_new.url += response.data.files[0].url;
[v2]array_new.push(response.data.files[0].url);
return array_new

а затем вызовите массив array_new через. .then, но ни один из них не работает отлично. Так что, может быть, кто-то даст мне подсказку или покажет, что я уже ответил на вопрос @Stackoverflow, который может быть полезен в моей ситуации.

  • 0
    кстати, даже с return data сразу после блока else я не могу просто main (). then (data => {}) `потому что это указывает на то, что я использую return рядом с функцией
  • 0
    Вероятно, проблема уже решена с помощью array.push (). Сейчас я проверяю это, но не стесняйтесь высказать свое мнение
Теги:
mongoose
asynchronous

2 ответа

1
Лучший ответ

Поскольку вы в основном имеете дело с обещаниями, вы можете реорганизовать свою логику функций для использования async, ожидая следующего:

function async main() {
    try {
        const servers = await server.find({locale: "en_GB"}).exec()
        const data = servers.map(async ({ name, slug }) => {
            const response = await rp({ url: slug })
            const { lastModified, url } = response.data.files[0]
            const count = await auctions.count({
                server: name,
                lastModified: { $gte: lastModified }
            })

            let result = {}
            if (count > 0) result = { name, url }

            return result
        }).filter(d => Object.keys(d).length > 0)

        Model.insertMany(data)
    } catch (err) {
        console.error(err)
    }   
}
  • 0
    Это именно то, что я пытаюсь сделать прямо сейчас, и как я хочу это увидеть. Очень скоро проверим и отметим это как полезное.
  • 1
    Я потратил почти неделю на первую версию этого сценария почти год назад. Он имеет 30 строк кода, даже без require и logger . Вы воссоздаете его за 20 минут. Я просто переписываю это, и это работает. Большое спасибо за этот снимок, вы заслужили 63к + репутацию в сообществе SW. Я буду очень благодарен, если вы укажете мне пару ссылок для практики async/await .
Показать ещё 1 комментарий
1

Ваша проблема связана с логикой, скрытой вашими обещаниями. Ваша main функция рекурсивно называет себя N раз, где N - количество серверов. Это ускоряет экспоненциальное использование памяти как процессом узла, так и MongoDB, обрабатывающим все запросы.

Вместо того, чтобы прыгать в async/await, начните с использования обещаний и дожидайтесь завершения серии N запросов до запуска другой партии. Вы можете использовать [Promise.all] для этого.

function main() {
  server.find({locale: "en_GB"}).exec(function (err, server) {
    // need to keep track of each promise for each server
    let promises = []

    for (let i = 0; i < server.length; i++) {
      let promise = rp({
        url: server[i].slug
      }).then(function(response) {
        // instead of nesting promises, return the promise so it is handled by 
        //  the next then in the chain.
        return auctions.count({
          server: server[i].name,
          lastModified: {
            $gte: response.data.files[0].lastModified
          }
        });
      }).then(function (docs) {
        if (docs > 0) {
          // do whatever you need to here regarding making requests and 
          //  inserting into DB, but don't call main() here.
          return requestAndInsert();
        }
      }).catch(function (error) {
          console.log(error);
      })
      // add the above promise to out list.
      promises.push(promise)
    }
    // register a new promise to run once all of the above promises generated
    //  by the loop have been completed
    Promise.all(promises).then(function () {
      // now you can call main again, optionally in a setTimeout so it waits a 
      //  few seconds before fetchin more data.
      setTimeout(main, 5000);
    })
  })
}

main()
  • 2
    Я, вероятно, описываю свою проблему с утечкой памяти немного неправильно, это не так плохо, как вы думаете. Единственная проблема в том, что старый работающий скрипт использует node-schedule / 10 mins вместо setTimeout и server.map вместо for of . Это помогает MongoDB съесть 4 ГБ + ОЗУ за эти 10 минут и постоянно держать его занятым. Это не было проблемой до вчерашнего дня, когда я хотел улучшить свой проект, но нашел только 20% свободной оперативной памяти для него. Кстати, вы правы насчет promises как я упоминал ранее, я не эксперт в этом. Я попробую это решение, прямо после того, как я закончу свое собственное. Спасибо за ваш вклад!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню