noquierouser
noquierouser

Reputation: 1003

Calling promise-driven functions regularly with setInterval and operating over its results

My goal is to make a video playlist generator that runs at specific times, along with a running clock and many other things that run periodically, but I'm stuck at the part where it generates the playlist at the times I define.

I'm using FileHound to walk a folder, which works whenever I call the find() method, but as it's Promise driven, its results aren't readily available to be used by the following console.log(), for example. Same thing happens with MediaInfo(), but this time I was able to work around it with async/await, which as far as I know is the actual way to use a Promise based function.

Now, as far as I can understand, the .each() and .then() method chaining are the ways to use the results from a Promise driven function, but that would quickly result in code repetition for every time I want to do the same thing in different places.

All being said, I think I'm off my track by very far, and despite my efforts I can't seem to find a clear way to achieve what I want, so I'm asking for help. This is the code I have so far, and I'm using the following npm packages:

const MediaInfo = require("node-mediainfo");
const Path = require("path");
const FileHound = require("filehound");
const Moment = require("moment");

const START_TIME = Moment();
const END_TIME = null;
const VIDEO_PATH = "videos";

let files = FileHound.create()
  .path(VIDEO_PATH)
  .depth(0);
let playlist = [];
let start_time = START_TIME.add(10, "seconds");

const ArrayShuffle = (array) => {
  for (let i = array.length - 1; i > 0; i--) {
    let j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
};

const MakePlaylist = async (file) => {
  let fileinfo = await MediaInfo(Path.resolve(file));
  let duration = fileinfo.media.track[0].Duration;
  let title    = fileinfo.media.track[0].Title || Path.basename(file, Path.extname(file));
  let artist   = fileinfo.media.track[0].Performer || null;

  playlist.push({
    path    : Path.resolve(file),
    title   : title,
    artist  : artist,
    duration: duration
  });
};

console.log(start_time.toLocaleString());

/* Main Loop */
setInterval(() => {
  if (Moment().isSame(start_time)) {
    console.log("First Run");
    files.find().each(MakePlaylist).then(ArrayShuffle);
    console.log(playlist);
  }

  if (Moment().isSame(Moment(start_time).add(30, "seconds"))) {
    console.log("Second Run");
    playlist = [];
    files.find().each(MakePlaylist).then(ArrayShuffle);
    console.log(playlist);
  }
}, 1);

Upvotes: 0

Views: 51

Answers (1)

Jamiec
Jamiec

Reputation: 136154

I would be tempted to put your logic into an async function, making use of await. and then simply recall your function later, instead of trying to do it in setTimeout

Assuming files.find() returns a promise....

const GeneratePlaylist = async () => {
    var files = await files.find();
    var playlist = [];
    for(var i=0;i<files.length;i++){
       playlist.push(await MakePlaylist(files[i]));
    }
    ArrayShuffle(playlist);
    return playlist;
}

Then you can use that from another async function, and you can recall it 1ms later with setTimeout (Note, you should make MakePlaylist return the new item, not push to a global array):

const doMyThing = async (){
  if (Moment().isSame(start_time)) {
    console.log("First Run");
    playlist = await GeneratePlaylist();
    console.log(playlist);
  }

  if (Moment().isSame(Moment(start_time).add(30, "seconds"))) {
    console.log("Second Run");
    playlist = [];
    playlist = await GeneratePlaylist();
    console.log(playlist);
  }

  setTimeout(doMyThing,1);
}

Below is a working example, where I've just faked up some of your functionality using Promises to simulate asynchronous work like finding files and loading the media info:

const ArrayShuffle = (array) => {
  for (let i = array.length - 1; i > 0; i--) {
    let j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
};

const MakePlaylist = async (file) => {
  let fileinfo = await new Promise(resolve => setTimeout(() => resolve({media:{track:[{Duration:1,Title:"Title",Performer:"Foo"}]}}),1000));
  let duration = fileinfo.media.track[0].Duration;
  let title    = fileinfo.media.track[0].Title || Path.basename(file, Path.extname(file));
  let artist   = fileinfo.media.track[0].Performer || null;

  return{
    path    : file,
    title   : title,
    artist  : artist,
    duration: duration
  };
};

const findFileMockup = () => {
   return new Promise(resolve => setTimeout(() => {
      resolve(["file1.txt","file2.txt","file3.txt"]);
   },500));
}

const GeneratePlaylist = async () => {
    var files = await findFileMockup(); // await files.find() in your code
    var playlist = [];
    for(var i=0;i<files.length;i++){
       playlist.push(await MakePlaylist(files[i]));
    }
    ArrayShuffle(playlist);
    return playlist;
}

const test = async () => {
  
    var playlist = await GeneratePlaylist();
    console.log(playlist);
  
}

test();

Upvotes: 1

Related Questions