0

Good day all.

Today i'm working in this complex script that makes request's to a site with server-side rendering, get's the HTML, breaks and grabs some data. The script has 4 phases: phaseOne, phaseTwo, phaseThree and phaseFour.

Which phases has a similar interface:

class PhaseOne {
  constructor(MAP) {
    this.MAP = MAP || MAP;
  }

  // All code related with the phase here.
  process() {}
}

So i'm working upon this MAP object in all phases, and i'm calling each phase in a stack, like this:

let phases = require('./phases');

[
  // 'Initial',
  'PhaseOne',
  'PhaseTwo',
  'PhaseThree',
  'PhaseFour',
].reduce((m, fn) => {
  return new phases[fn](m).process();
}, MAP);

Everything is working fine. My problem is: Some phases are REALLY slow.. all the program will take 30 minutes to finish.. and i would like to see in my terminal the percentage of each phase.

Like:

PhaseOne: 10%
PhaseOne: 11%
PhaseOne: 12%

But i don't have any idea and i can't find a good tutorial to do that..

Currently inside my process functions i have for loops, if statements.. in general i'm using imperative style..

An example of PhaseOne:

   // In this phase we need the property string in MAP.aguia01 to
    // assign the first context and extract the data with regex.
    if (typeof this.MAP.aguia01.string === 'undefined') {
      cli.fatal(
        'MAP doesn\'t have the necessary properties to run in Aguia01 phase. Exiting...'
      );
    }

    for (let month of months) {
      this.MAP.aguia01.string += requests.aguia01.apply(this, [month]);
    }

    for (let info of patterns.aguia01.infos) {
      this.MAP.aguia01.infos[info.name] = (
        this.MAP.aguia01.string.match(info.pattern)
      )[1];
    }

    for (let line of patterns.aguia01.lines) {
      this.MAP.aguia01.lines[line.name] = (
        this.MAP.aguia01.string.match(line.pattern)
      )[1];
    }

So.. Is it possible to do what i want with imperative style?

Thanks.

2 Answers2

0

There is the progress package but it's only up to you how you define "progress". You define a number of ticks corresponding to the completed state and then, you just call a method on the progress bar to make it "progress". An example:

var ProgressBar = require('progress');

// 10 ticks to complete the task
var bar = new ProgressBar(':bar', { total: 10 });
var timer = setInterval(function () {
  // make the bar tick(), each call will make a 10% progress
  bar.tick();
  if (bar.complete) {
    console.log('\ncomplete\n');
    clearInterval(timer);
  }
}, 100);
Shanoor
  • 13,344
  • 2
  • 29
  • 40
0

How about keeping a context object for progress outside of your reduce call? You could make it an event emitter, and then pass it in to your process function. Inside your process function you could emit progress events, which could then be logged. Perhaps something like this:

let phases = require('./phases');

//set up
let progressMonitor =  new require('events')
progressMonitor.on('progress', percentDone => {
  console.log(percentDone + '% done')
})

// your existing code
[
  // 'Initial',
  'PhaseOne',
  'PhaseTwo',
  'PhaseThree',
  'PhaseFour',
].reduce((m, fn) => {
  return new phases[fn](m).process(progressMonitor);
}, MAP);

and then inside your process functions:

class PhaseOne {
 constructor(MAP) {
    this.MAP = MAP || MAP;
  }

  // All code related with the phase here.
  process(progressMonitor) {
    //do work
    progressMonitor.emit('progress', 10)
    //more work
    progressMonitor.emit('progress', 15)
    //much work
    progressMonitor.emit('progress', 75)
  }
}