На самом деле я не уверен, что Название моего вопроса "правильно", если у вас есть идея с ним, вы можете оставить комментарий, и я его переименую.
Я пытаюсь переписать мою старую функцию, которая делает 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, который может быть полезен в моей ситуации.
Поскольку вы в основном имеете дело с обещаниями, вы можете реорганизовать свою логику функций для использования 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)
}
}
require
и logger
. Вы воссоздаете его за 20 минут. Я просто переписываю это, и это работает. Большое спасибо за этот снимок, вы заслужили 63к + репутацию в сообществе SW. Я буду очень благодарен, если вы укажете мне пару ссылок для практики async/await
.
Ваша проблема связана с логикой, скрытой вашими обещаниями. Ваша 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()
node-schedule
/ 10 mins вместо setTimeout
и server.map
вместо for of
. Это помогает MongoDB съесть 4 ГБ + ОЗУ за эти 10 минут и постоянно держать его занятым. Это не было проблемой до вчерашнего дня, когда я хотел улучшить свой проект, но нашел только 20% свободной оперативной памяти для него. Кстати, вы правы насчет promises
как я упоминал ранее, я не эксперт в этом. Я попробую это решение, прямо после того, как я закончу свое собственное. Спасибо за ваш вклад!
return data
сразу после блокаelse
я не могу просто main (). then (data => {}) `потому что это указывает на то, что я используюreturn
рядом с функцией