Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weird Behavior of MultiProgressBar #22

Open
bjesuiter opened this issue Feb 21, 2023 · 2 comments
Open

Weird Behavior of MultiProgressBar #22

bjesuiter opened this issue Feb 21, 2023 · 2 comments

Comments

@bjesuiter
Copy link
Contributor

I'm still trying to get my multi-progress-bar to work with this library.

Simplified Working Example

The sleep() calls in there makes sure that my updates will be scheduled slower than the drawing interval.

import { MultiProgressBar } from "https://deno.land/x/[email protected]/mod.ts";
import { sleep } from "https://deno.land/x/sleep/mod.ts";

const multibar = new MultiProgressBar({
  title: "Multi-Progress Bars",
  complete: "=",
  incomplete: "-",
  interval: 1,
  display: "[:bar] :text :percent :time :completed/:total",
});

multibar.render([
  { text: "progress_1", completed: 1 },
]);

await sleep(0.002);

multibar.render([
  { text: "progress_1", completed: 2 },
  { text: "progress_2", completed: 1 },
]);

await sleep(0.002);

multibar.render([
  { text: "progress_1", completed: 2 },
  { text: "progress_2", completed: 2 },
]);

await sleep(0.002);

multibar.render([
  { text: "progress_1", completed: 3 },
  { text: "progress_2", completed: 2 },
  { text: "progress_3", completed: 1 },
]);

Output

Multi-Progress Bars
[==------------------------------------------------] progress_1 3.00% 0.0s 3/100
[=-------------------------------------------------] progress_2 2.00% 0.0s 2/100
[=-------------------------------------------------] progress_3 1.00% 0.0s 1/100

Problematic Code

Here is a file with 3 mocked progress readables:
https://github.com/codemonument/deno_downstream/blob/main/test/units/multiProgressCliRenderer.test.ts

When I activate the simpleCallbackTarget(), I get a stream of all the state events based on the aforementioned format:

[
  { text: "progress_1", completed: 3 },
  { text: "progress_2", completed: 2 },
  { text: "progress_3", completed: 1 },
]

But when I activate the multiProgressCliRenderer() it only outputs two progress bars at first until they finished and then outputs the third one all at once, like this:

While running:

Multi-Progress Bars
[============================================------] progress_1 87.00% 4.5s 87/100
[============================================------] progress_2 87.00% 4.5s 87/100

When Finished:

Multi-Progress Bars
[==================================================] progress_1 100.00% 5.1s 100/100
[==================================================] progress_2 100.00% 5.1s 100/100
[==================================================] progress_3 100.00% 5.1s 100/100

Test: Adding a sleep

I also tried to add a await sleep(0.002) together with 'interval: 1' in 'multiProgressCliRenderer.ts':
Source File: https://github.com/codemonument/deno_downstream/blob/main/lib/streamAdapters/MultiProgressCliRenderer.ts

return new WritableStream({
    start(_controller) {
      // do init logic, if needed
    },
    async write(state: MultiProgressState, _controller) {
      await sleep(0.002);
      multibar.render(state);
    },
    close() {
    },
    abort(reason) {
      console.error("Stream error:", reason);
    },
  });

But this did not work.

Current State

  1. I think, my sleep is not working correctly here, since it might run in 'parallel' to all the other invocations of 'write' on this writable stream. => So I have to figure something out for that
  2. But I also think that the API for drawing here is inconvenient and that It would be nice to brainstorm with you how to improve at least error reporting, when render requests are dropped by the progress library because of being smaller than the interval.
@fuxingZhang
Copy link
Collaborator

You can try interval: 0

const multibar = new MultiProgressBar({
  title: "Multi-Progress Bars",
  complete: "=",
  incomplete: "-",
  interval: 0,
  display: "[:bar] :text :percent :time :completed/:total",
});

I have been out of JS/TS for more than three years.
I can only deal with simple issue.
Your pull requests are welcome!

Suggestion

Replace sleep to delay(which is in Deno Standard Modules)

setTimeout in deno test will throw error

https://deno.com/blog/v1.20#tracing-operations-while-using-deno-test

Replace https://deno.land/x/sleep/mod.ts to https://deno.land/[email protected]/async/delay.ts

import { sleep } from "https://deno.land/x/sleep/mod.ts";

https://github.com/michael-spengler/sleep/blob/master/sleep.ts

export function sleep(seconds: number) {
  return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
}

import { delay } from "https://deno.land/[email protected]/async/delay.ts";

delay is better(clearTimeout,Deno.unrefTimer).

Deno Standard Modules
These modules do not have external dependencies and they are reviewed by the Deno core team. The intention is to have a standard set of high quality code that all Deno projects can use fearlessly.

https://github.com/denoland/deno_std/blob/main/async/delay.ts

// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.

export interface DelayOptions {
  /** Signal used to abort the delay. */
  signal?: AbortSignal;
  /** Indicates whether the process should continue to run as long as the timer exists.
   *
   * @default {true}
   */
  persistent?: boolean;
}

/**
 * Resolve a Promise after a given amount of milliseconds.
 *
 * @example
 *
 * ```typescript
 * import { delay } from "https://deno.land/std@$STD_VERSION/async/delay.ts";
 *
 * // ...
 * const delayedPromise = delay(100);
 * const result = await delayedPromise;
 * // ...
 * ```
 *
 * To allow the process to continue to run as long as the timer exists. Requires
 * `--unstable` flag.
 *
 * ```typescript
 * import { delay } from "https://deno.land/std@$STD_VERSION/async/delay.ts";
 *
 * // ...
 * await delay(100, { persistent: false });
 * // ...
 * ```
 */
export function delay(ms: number, options: DelayOptions = {}): Promise<void> {
  const { signal, persistent } = options;
  if (signal?.aborted) {
    return Promise.reject(new DOMException("Delay was aborted.", "AbortError"));
  }
  return new Promise((resolve, reject) => {
    const abort = () => {
      clearTimeout(i);
      reject(new DOMException("Delay was aborted.", "AbortError"));
    };
    const done = () => {
      signal?.removeEventListener("abort", abort);
      resolve();
    };
    const i = setTimeout(done, ms);
    signal?.addEventListener("abort", abort, { once: true });
    if (persistent === false) {
      try {
        // @ts-ignore For browser compatibility
        Deno.unrefTimer(i);
      } catch (error) {
        if (!(error instanceof ReferenceError)) {
          throw error;
        }
        console.error("`persistent` option is only available in Deno");
      }
    }
  });
}

@bjesuiter
Copy link
Contributor Author

Ok, I understand this:

I have been out of JS/TS for more than three years.
I can only deal with simple issue.
Your pull requests are welcome!

Thank you for your suggestions, I'll try to find and fix the problem!
If I have anything useful, I'll give you a pr :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants