FunctionFlow ®

Control JS function execution flow

Introduction

One of the most important DEX8 helper libraries is FunctionFlow ®. The FunctionFlow controls whether functions will be executed in serial or in parallel sequence. It gives the possibility for iterative repetition of serial or parallel function sequence. It defines time delays between every function execution step. Finally, it controls the whole process to start, stop or pause.
There is some similarity with the async npm library but FunctionFlow is much more powerful because it uses JS Promises and async/await.

Although it is not obligatory for developers to use FunctionFlow, this library will speed up DEX8 Task coding and make code more readable. The library is free to use and it has AGPL-3.0 License. Developers can use it in any project, and it's not only designed for DEX8 Tasks.

FunctionFlow is part of DEX8-SDK and is available via NPM
$ npm install dex8-sdk
[ https://www.npmjs.com/package/dex8-sdk ]

How to use FunctionFlow out of DEX8
-----------------------------------------------------
const { FunctionFlow } = require('dex8-sdk');
const ff = new FunctionFlow({debug: true, msDelay: 1300});
ff.serial([func1, func2, func3]);

When coding DEX8 Task a FunctionFlow class instance is already injected so it can be used inside functions as shown in these examples: https://github.com/smikodanic/dex8-examples/

main.js
--------------------------------------------------------
const f0 = require('./f0');
const f1 = require('./f1');
const f2 = require('./f2'); // contains go(1)
const f3 = require('./f3');
const f4 = require('./f4'); // contains go(6)
const f5 = require('./f5');
const f6 = require('./f6');



module.exports = async (input, lib) => {
  const echo = lib.echo;
  const ff = lib.ff;
  ff.setOpts({debug: false, msDelay: 1300});
  ff.xInject(input);
  ff.libInject(lib);

  echo.log('input::', input);

  const y = await ff.serial([f0, f1, f2, f3, f4, f5, f6]);

  echo.log('output::', y);
  return y; // or return ff.x;
};

Class "FunctionFlow"

options

when used out of DEX8 platform
----------------------------------------
const { FunctionFlow } = require('dex8-sdk');
const opts = {debug: true, msDelay: 1300};
const ff = new FunctionFlow(opts);
when used in DEX8 platform
----------------------------------------
module.exports = async (input, lib) => {
  const ff = lib.ff;
  const opts = {debug: true, msDelay: 1300};
  ff.setOpts(opts);
};

Properties

Property Description Type Default
debug Whether to use internal debugger or not. A property in options object. boolean false
msDelay Delay in milliseconds after each function. A property in options object. number 0
status Process status 'start', 'stop', 'pause'. string start
iteration Iteration number when repeat method is used. number 0
x Context value. It is input value modified by the process. any
lib A set of injected or added libraries. object

Methods

GENERAL METHODS

setOpts(opts)

The opts parameter has two properties: debug and msDelay.
ff.setOpts({debug: false, msDelay: 400});

xInject(input)

Injects input value so it can be used in any function (see x param in the example below). Input is the initial value which is changed during the process execution and as a final result gives output.

main.js
----------------------------------------
const f1 = require('./f1.js');
const f2 = require('./f2.js');
const f3 = require('./f3.js');

module.exports = async (input, lib) => {
  const ff = lib.ff;
  ff.setOpts({debug: false, msDelay: 1200});
  ff.xInject(input);
  ff.libInject(lib);

  const y = await ff.serial([f1, f2, f3, f1]);
  await ff.delay(3400);

  echo.log('output::', y);
  return y; // or return ff.x;
};

f1.js
----------------------------------------
function f1(x, lib) {
  const echo = lib.echo;

  x.a = x.a + 1;
  echo.log('f1:: ', x.a);
  return x;
}

module.exports = f1;

xRemove()

This sets the input as undefined. If during the execution process it is needed to reset the input use:
ff.xRemove();

libInject(lib)

Injects library so it can be used in any function (see lib param in the example above). Previously added libraries will be removed.
ff.libInject(lib);

libAdd(lib)

Add new libraries to the existing set of libraries. New libraries will be appended along with previously added libraries.
Example 1: 035ff_libAdd
Example 2:
main.js
----------------------------------------
const BPromise = require('bluebird');

module.exports = async (input, lib) => {
  const ff = lib.ff;
  ff.setOpts({debug: false, msDelay: 1200});
  ff.xInject(input);
  ff.libInject(lib);
  ff.libAdd({BPromise});
};

f1.js
----------------------------------------
module.exports =  async (x, lib) => {
  const BPromise = lib.BPromise;
  lib.echo.objekt(BPromise);
  return x;
}

libRemove()

Removes all libraries.
ff.libRemove();

libList()

Returns an array with all library names. For example, it returns: ["ff","echo","mongo","HttpClient","Rand","MyLib"]
ff.libList();

FUNCTION BUNDLE METHODS

serial(funcs)

Execute an array of functions serially with a time delay after every function. The time delay is defined by msDelay property of options parameter.
ff.serial([f0, f3, f2]);
input------>|--f0-->|msDelay|--f3-->|msDelay|--f2-->|msDelay|------>output
Example: 010ff_serial

one(func)

Executes just one function with a time delay after it is executed. The time delay is defined by msDelay.
ff.one(f1);
input------>|--func1-->|msDelay|-------->output
Example: 011ff_one

parallelAll(funcs)

Executes functions simultaneously. All functions must return fulfilled promises because Promise.all() is used to return final results.
All functions have same input value. Returned (output) value is an array of resolved values.
ff.parallelAll([f2, f4, f8]);
         --> |--------- f2(x) ---------->---|
-- input --> |--------- f4(x) ------->------|msDelay|---> [r2, r4, r8]
         --> |--------- f8(x) ------------->|
        
Example: 015ff_parallelAll

parallelRace(funcs)

Executes functions simultaneously. Fastest function must return fulfilled promise because Promise.race() is used to return final result.
All functions have the same input value. Returned (output) value is the value from the first resolved function.
ff.parallelRace([f2, f4, f8]);
         --> |--------- f2(x) ---------->---|
-- input --> |--------- f4(x) ------->------|msDelay|---> r4
         --> |--------- f8(x) ------------->|
        
Example: 016ff_parallelRace

ITERATION METHODS

repeat(n)

Repeats last executed FunctionFlow bundle method ā€˜nā€™ times. (Bundle method means serial or parallel method.)
The time delay is defined by msDelay.
ff.repeat(5);
input -->|----------serial------------->|----> output
         |                             n|
         |<---------repeat n------------|
        
Example 1: 020ff_repeat_serial
await ff.serial([f1, f2, f3]);
const y = await ff.repeat(3); // repeats ff.serial 3 times
Example 2: 021ff_repeat_parallelRace

COMMAND METHODS

stop()

Stops the execution of all functions used in bundle methods (serial, one or parallel). Condition: status = 'start'.
ff.stop();

pause()

Pauses function flow execution used in bundle methods (serial, one or parallel). Condition: status = 'start'.
ff.pause();

start()

Starts/restarts function flow execution if it's previously been stopped or paused. Condition: status = 'pause' | 'stop'.
ff.start();

go(goTo)

Go to the function used in the serial(funcs) method. Parameter goTo is the index number of funcs array and the condition 0 <= goTo < funcs.length must be fulfilled. When the execution of that function is finished, continue with the next function in funcs array.
It's usually used to go to another function which is in the serial() method.
ff.go(5);

Example: 025ff_go
f2.js
------------------
module.exports = (x, lib) => {
  x++;
  lib.echo.log('f2:: ', x);
  if (x < 13 ) { lib.ff.go(1); } // go back to f1
  return x;
};

next()

Stop execution of all funcs functions in serial(funcs) method and continue with the next serial, one or parallel bundle method.
It will work only inside a function used in the serial() method. A parameter is not needed for this method.
ff.next();

Example: 027ff_next
f2.js
------------------
module.exports = (x, lib) => {
  x++;
  lib.echo.log('f2::', x);
  lib.ff.next();
  return x;
};

jump(jumpTo)

Jump to iteration number defined in the repeat(n) method. When that iteration is executed continue with the next iteration in repeat(n) method.
Parameter jumpTo is the iteration number and the condition 0 < jumpTo <= n must be fulfilled.
It's usually used to skip some iterations under certain conditions. To get a current iteration number use ff.iteration.
ff.jump(3);

Example: 026ff_jump
f2.js
------------------
module.exports = (x, lib) => {
  x++;
  lib.echo.log('f2::', x);
  if (lib.ff.iteration === 2) { lib.ff.jump(10); } // on 2nd iteration jump to last iteration
  return x;
};

break()

Breaks all iterations in repeat(n) method. Parameter is not required. It sets this.jumpTo = Infinity
It's usually used inside the function to stop all iterations.
ff.break();
func.js
------------------
module.exports = (x, lib) => {
  x++;
  if (x.val > 2) { lib.ff.break(); } // stop iterations defined in main.js by ff.repeat(n);
  return x;
};

TIME METHODS

delay(ms)

Delay in miliseconds.
ff.delay(3400);

delayRnd(msMin, msMax)

Random delay from msMin to msMax.
ff.delayRnd(3000, 5000);