visionmedia / node-progress

Flexible ascii progress bar for nodejs
MIT License
2.98k stars 221 forks source link

Setting `curr` property breaks `:eta` and `:rate` #159

Open kaatt opened 7 years ago

kaatt commented 7 years ago

Setting the curr property breaks the :eta and :rate tokens.

To reproduce, edit examples/customtokens.js to include the curr property:

var bar = new ProgressBar(':percent eta: :eta downloading :current/:total :file :rate', {
  total: list.length, curr: 2
})
$ node examples/customtokens.js # Before
30% eta: 2.4 downloading 3/10 image03.jpg 3
^C
$ node examples/customtokens.js # After
100% eta: 0.0 downloading 10/10 image10.jpg NaN

:eta is always 0.0 and :rate is NaN

The bug isn't present when curr: 0

FoxxMD commented 7 years ago

Experiencing this as well.

Workaround is not setting curr and then just ticking the bar the value that would have been set to curr. Not great but not bad for long-running operations.

mildmojo commented 4 years ago

The problem is that the library doesn't record the download start time when you specify curr. When that's fixed, the rate and eta calculations will need some work to take a starting curr value into account, too.

Workaround

The most basic workaround is to set the ProgressBar instance's start attribute to Date.now(). That'll keep :rate and :eta from erroring, but the calculations will act like you made curr amount of progress in zero time at an infinite rate, and it'll take a long time for the rate and ETA to settle.

Here's a workaround using custom tokens :myRateKB and :myETA. I'm displaying file transfer progress. The downit library provides a progress callback with total-bytes-received in gotBytes, and I'm diffing that value against the last-seen gotBytes to get a delta to feed to bar.tick().

  const lastDownloadSize = startingFileDiskSize;

  bar = new ProgressBar(`  [:bar] :myRateKB KiBps :percent :myETAs remaining`, {
    width: 40,
    curr: startingFileDiskSize,
    total: remoteSize
  });

  // Just setting `bar.start` will make :rate and :eta work, but will act like
  // the first `curr` bytes were transferred in zero time at infinite rate.
  bar.start = Date.now();

  downit(url, outPath, {
    onprogress: gotBytes => {
      const elapsedSecs = (Date.now() - bar.start) / 1000;
      const sessionTotalSize = remoteSize - startingFileDiskSize;
      const sessionProgress = gotBytes - startingFileDiskSize;
      bar.tick(gotBytes - lastDownloadSize, {
        myRateKB: Math.round((sessionProgress / 1024) / elapsedSecs),
        myETA: Math.round(elapsedSecs * (sessionTotalSize / sessionProgress - 1))
      });
      lastDownloadSize = got;
    }
  });