Итак, у меня есть этот код:
for (let i = 0; i < array.length; i++) {
let url = array[i];
YTDL.getInfo(url, function(err, info) {
if (err) {
message.channel.send("There was an error while checking information about a video, try again soon.");
throw err;
}
songEmbed.addField("Title:", info.title);
if (i == array.length - 1) message.channel.send(songEmbed);
});
}
И проблема в том, что функция внутри YTDL.getInfo() вызывается после завершения цикла for, но мне нужно вызывать message.channel.send(songEmbed) только после последней итерации (см. Код), я старался изо всех сил, чтобы Решите это самостоятельно, например, с помощью array.forEach(), но я не могу понять, как передать правильный индекс элемента в эту функцию внутри YTDL.getInfo(). Надеюсь, вы меня понимаете.
Согласно примерам npm, getinfo возвращает обещание. Решение с Promise.all, Array # map и Array # forEach
Promise.all
гарантирует, что обратный вызов then
будет вызван после завершения каждого запроса.
values
представляет array
info
о каждом запросе.
Promise.all(array.map(url => YTDL.getInfo(url).catch(e => e))
.then(results=>{
//all the requests have ended
//add all titles to songEmbed
results.forEach(result=>{
if(result instanceof Error){
console.warn(result.message);
} else {
songEmbed.addField("title:", result.title)
}
});
//send songEmbed
message.channel.send(songEmbed)
}).catch(err=>console.warn(err.message));
catch
в конце. Тем не менее, это все или ничего. Это означает, что в случае сбоя одного из запросов все завершается неудачей. Кроме того, вы можете проверить этот вопрос, который, вероятно, то, что вы ищете: stackoverflow.com/questions/30362733/… . Я также добавлю решение к моему ответу через мгновение.
Поскольку код выполняется асинхронно, цикл завершается до завершения функции.
Вы можете использовать обещания или обратные вызовы для простоты. Если вы тоже не знаете, я рекомендую вам начать использовать обратный вызов, и когда вы сможете справиться с этим, начните использовать Promises, Async и Await.
Примером того, как вы можете это сделать, было вставить ваш код в функцию с обратным вызовом в качестве параметра.
function magic_func(array, callback) {
for (let i = 0; i < array.length; i++) {
let url = array[i];
YTDL.getInfo(url, function(err, info) {
if (err) {
message.channel.send("There was an error while checking information about a video, try again soon.");
throw err;
}
songEmbed.addField("Title:", info.title);
if (i == array.length - 1) {
message.channel.send(songEmbed)
callback()
}
});
}
}
Когда обратный вызов вызван, вы можете продолжить делать следующий шаг, например:
magic_func(array, function() {
//ADD your next code here
})
То, что вы испытываете, является одной из самых неприятных/мощных вещей с javascript, асинхронными методами. Я предполагаю, что в методе getInfo вы делаете какую-то форму вызова ajax. Javascript, чтобы сэкономить время и ресурсы, позволит перевести вызов в фоновый режим, чтобы он не блокировал выполнение следующего кода. Обычно это хорошо, так как позволяет одновременно запускать большое количество javascript, и страница загружается намного быстрее, чем если бы она была синхронной. Но для вас это означает, что при доступе к info.title он фактически не был установлен. Простой и грязный способ исправить это - добавить вызов setTimeout вокруг материала, который вы хотите вызывать в цикле for. Что-то вроде:
setTimeout(function(){
if (err) {
message.channel.send("There was an error while checking information about a video, try again soon.");
throw err;
}
songEmbed.addField("Title:", info.title);
if (i == array.length - 1) message.channel.send(songEmbed);
}, 3000);
Существуют лучшие способы решения этой проблемы, как указали некоторые другие ответы, но это должно продемонстрировать проблему немного лучше.
Требуется время, чтобы получить ответ от одного URL, не говоря уже о нескольких URL в вашем массиве. Это асинхронный вызов, поэтому для ожидания ответа следует использовать Promises. Существует множество примеров. Это должно быть полезно https://medium.freecodecamp.org/javascript-from-callbacks-to-async-await-1cc090ddad99
Попробуйте понять основы синхронизации/асинхронности в JavaScript. Для этого есть самые популярные ресурсы:
https://www.youtube.com/watch?v=cCOL7MC4Pl0
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
https://www.youtube.com/watch?v=8aGhZQkoFbQ
Ваша ошибка - запуск асинхронной функции из цикла for for sync, в результате даже внутри первого запуска функции вы получите индекс последнего элемента. Проведите время со ссылками, которые я вам отправил, и с этой темой, она вам очень поможет.
А как насчет вашего вопроса и решения для него просто используйте async для цикла, ссылки для ознакомления:
https://hackernoon.com/async-await-essentials-for-production-loops-control-flows-limits-23eb40f171bd
https://codeburst.io/asynchronous-code-inside-an-array-loop-c5d704006c99
getInfo
работает асинхронно.