@js-bits/executor
TypeScript icon, indicating that this package has built-in type declarations

2.0.13 • Public • Published

Abstract Executor class

Executor is a class derived from Promise and it has similar behavior but with one major difference: asynchronous operation that ties an outcome to a promise is decoupled from the constructor.

Executor also has two useful features:

  • Built-in monitoring of execution time
  • Optional rejection by a specified timeout

The package additionally includes a simple executor implementation called Receiver.

Installation

Install with npm:

npm install @js-bits/executor

Install with yarn:

yarn add @js-bits/executor

Import where you need it:

import { Executor, Receiver } from '@js-bits/executor';

or require for CommonJS:

const { Executor, Receiver } = require('@js-bits/executor');

How to use

Since asynchronous operation is decoupled it won't be executed automatically when a new Executor gets created. Instead you have to call .execute() method explicitly.

const asyncOperation = new Executor((resolve, reject) => {
  // perform some asynchronous actions
  // ...
  // and resolve the promise when the operation is completed
  resolve(123);
});
// ...
// execute the operation where necessary
asyncOperation.execute();
// ...
// handle the result
asyncOperation.then(result => {
  console.log('result', result); // 123
});

Execution timings

There are five metrics available any time through timings property:

  • CREATED
  • EXECUTED
  • RESOLVED
  • REJECTED
  • SETTLED (equals to either RESOLVED or REJECTED)

Use Executor.STATES static enum property in order to access them.

// create a new class of Executor
class DOMReadyReceiver extends Executor {
  constructor(...args) {
    super((resolve, reject) => {
      if (document.readyState === 'complete' || document.readyState === 'interactive') {
        resolve(true);
      } else {
        window.addEventListener('DOMContentLoaded', () => {
          resolve(false);
        });
      }
    }, ...args);
    this.execute(); // let's execute it right away for the sake of demo
  }
}

// create local state constants for convenience
const { CREATED, EXECUTED, RESOLVED } = DOMReadyReceiver.STATES;

(async () => {
  // create an instance of the class to be able to handle DOM Ready event
  const domReady = new DOMReadyReceiver();
  // wait for it to be resolved
  const isDomReady = await domReady;

  // check execution metrics
  console.log(`DOMReadyReceiver created in ${domReady.timings[CREATED] / 1000} s`); // DOMReadyReceiver created in 0.629 s
  console.log(`${isDomReady ? 'DOM ready' : 'DOMContentLoaded'} in ${domReady.timings[RESOLVED] / 1000} s`); // DOMContentLoaded in 0.644 s
  console.log(`Delay: ${domReady.timings[RESOLVED] - domReady.timings[EXECUTED]} ms`); // Delay: 15 ms
})();

Timeout

You can use optional timeout parameter to set maximum allowable execution time for the asynchronous operation. There are 2 types of timeout supported:

Hard timeout. The executor will be automatically rejected when specified timeout is exceeded. It can be set by an integer number passed as a value of timeout parameter.

import Timeout from '@js-bits/timeout';

const asyncOperation = new Executor(
  (resolve, reject) => {
    setTimeout(() => {
      resolve(); // resolve the operation in about 1 second
    }, 1000);
  },
  {
    timeout: 100, // set timeout to 100 ms
  }
);

(async () => {
  const { EXECUTED, RESOLVED, SETTLED } = Executor.STATES;

  asyncOperation.execute();
  try {
    await asyncOperation;
    console.log(`Resolved in ${(asyncOperation.timings[RESOLVED] - asyncOperation.timings[EXECUTED]) / 1000} s`);
  } catch (reason) {
    // TimeoutExceededError: Operation timeout exceeded
    if (reason.name === Timeout.TimeoutExceededError) {
      console.log(`Timed out in ${asyncOperation.timings[SETTLED] - asyncOperation.timings[EXECUTED]} ms`); // Timed out in 104 ms
    }
  }
})();

Soft timeout. Can be set by a Timeout instance passed as a value of timeout parameter. The executor won't be rejected automatically. The timeout must be handled externally.

import Timeout from '@js-bits/timeout';

const asyncOperation = new Executor(
  (resolve, reject) => {
    setTimeout(() => {
      resolve('Success!!!'); // resolve the operation in about 1 second
    }, 1000);
  },
  {
    timeout: new Timeout(100),
  }
);

(async () => {
  asyncOperation.execute();
  asyncOperation.timeout.catch(reason => {
    if (reason.name === Timeout.TimeoutExceededError) {
      // you can report that operation has timed out
      console.log('Operation has exceeded specified timeout.'); // Operation has exceeded specified timeout.
    }
  });
  // operation can still continue
  console.log(await asyncOperation); // Success!!!
})();

Receiver

Receiver does not accept any executor function which basically means that it doesn't perform any actions by itself. Receiver can be used to asynchronously assign a value to some variable or indicate some event.

const someAsyncValue = new Receiver();
const { EXECUTED, RESOLVED } = Receiver.STATES;

(async () => {
  setTimeout(() => {
    someAsyncValue.resolve(123);
  }, 1000);

  console.log(`Received value: ${await someAsyncValue}`); // Received value: 123
  console.log(`It took ${someAsyncValue.timings[RESOLVED] - someAsyncValue.timings[EXECUTED]} ms to receive the value`); // It took 1005 ms to receive the value
})();

Notes

  • Loader - an implementation of Executor for HTTP requests.

Dependencies (4)

Dev Dependencies (9)

Package Sidebar

Install

npm i @js-bits/executor

Weekly Downloads

14

Version

2.0.13

License

ISC

Unpacked Size

50 kB

Total Files

27

Last publish

Collaborators

  • apolo-gh