Reputation:
server.queue.forEach(function(q) {
YTDL.getInfo(q, (error, info) => {
console.log(info["title"]);
message.reply('"' + info["title"] + '"');
});
});
for (var i = 0; i < server.queue.length; i++) {
YTDL.getInfo(server.queue[i], (error, info) => {
console.log(info["title"]);
message.reply('"' + info["title"] + '"');
});
}
I'm creating a music bot for a VoIP called Discord using Node.js and whenever either of the loops above execute, they print in a random order. How do I make it so that they are printed sequentially (server.queue[0], server.queue[1], server.queue[2]...)?
YTDL
is a package called ytdl-core that downloads YouTube videos as well as display the info such as the title of a video using the video link. server.queue
is an array of YouTube video links.
Upvotes: 3
Views: 9493
Reputation: 19372
Just to add why you try to make it sequential: YouTube has rate limits and etc limitations which can be easily broken if there will be parallel calls.
So for that reason we are not using Promise.all
or .allSettled
.
Solution to question based on modules and language features of stack for the year 2017:
install: npm i --save async
and the code:
const async = require('async');
async.eachSeries(
server.queue,
(q, next) => {
YTDL.getInfo(q, (error, info) => {
console.log(info["title"]);
message.reply('"' + info["title"] + '"');
next();
});
}
});
for..loop
is not good solution for asynchronous stuff - it will call them and run next statements that comes after for loop.
Question is dated for the year 2017, for that moment there was no proper support of async/await methods and etc. Plus people were staying at LTS versions of Node.js which slowed down progress.
Upvotes: 2
Reputation: 609
I was struggling with this problem for a while to find the best solution until I found out the built-in yield
feature represented in ECMA6. so using gen-run
library you can do the following:
let run = require('gen-run');
function notSequential(){
for (let i = 0; i < 3; i++)
f(i * 1000);
console.log("it won't print after for loop");
}
function sequential(){
run(function*(){
for (let i = 0; i < 3; i++)
yield changedF(i * 1000);
console.log("it will print after for loop");
});
}
function f(time) {
setTimeout(function(){
console.log(time);
}, time);
}
function changedF(time) {
return function (callback) {
setTimeout(function(){
console.log(time);
callback();
}, time);
}
}
notSequential();
it won't print after for loop 0 1000 2000
sequential();
0 1000 2000 it will print after for loop
Upvotes: 0
Reputation: 7292
If you don't want to use the async
library, you can use Promise.all
like this
const youtubeData = [];
for (var i = 0; i < server.queue.length; i++) {
youtubeData.push(YTDL.getInfo(server.queue[i]));
}
Promise.all(youtubeData).then((values) => {
// These values will be in the order in which they are called.
});
Note that Promise.all
will wait for all the queries to finish or reject everything whenever one request fails. Look at your use case and select accordingly.
As per library, it returns promise if no callback is provided
ytdl.getInfo(url, [options], [callback(err, info)])
Use this if you only want to get metainfo from a video. If callback isn't given, returns a promise.
Upvotes: 0