
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
A small JavaScript callbacks synchronizer for the browser and Node.js. A powerful alternative to using Promises, Flow.js offers you greater control, parallel execution, cleaner code and consistency across platforms.
Flow.js is a small yet powerful callback synchronizer for browsers and Node.js. A powerful alternative to Promise based structure, Flow.js offers you greater control, cleaner syntax and consistency across various platforms, all in a tiny package with virtually no overhead.
new Flow(
[
{fn: loadTemplate, success: parseTemplate},
{fn: getUserData, success: processUserData},
{fn: renderUI, sync: true},
{fn: getUserAlbums, success: processUserAlbums},
{fn: getAlbumImages, success: renderAlbumImages, sync: true}
]).execute();
To use Flow.js in Node.js projects, install its npm package.
npm install flow-sync
Usage:
var Flow = require('flow-sync');
new Flow([...]).execute();
When creating a new Flow object you provide it with an array of tasks.
A task can be a function with callbacks (an AJAX call for example) or another sub-flow.
Flow is not a queue - tasks in the flow will execute in parallel by default.
A task is an object with a number of attributes that define its behavior.
Example:
{fn: getUserAlbums, success: processUserAlbums, fail: onError}
| Property | Description | Accepts | Default |
|---|---|---|---|
fn | A function to be executed. | function | N/A |
flow | A sub-flow to be executed. If provided, fn will be ignored. | new Flow() | N/A |
success | A callback function to execute when the task is to be completed. | function | Empty function(){} |
fail | A callback function to execute when if the task fails for any reason. Under default conditions executing fail() will stop the flow. | function | Empty function(){} |
sync | If set as true will cause the task to await the completion of all previous tasks in the flow before executing. | boolean | false or true if defaultSync options is used |
failRetries | The number of times to repeat the task in case of failing. | integer | 0 |
retryInterval | An interval (in milliseconds) between retry attempts. Used in conjunction with failRetries. | integer or function | 0 |
continueOnFail | If set as true the failing of the task will not cause the entire flow to fail. | boolean | false |
Accepts:
function(success, fail)
A function to be executed. The function receives a mandatory success and an optional fail functions.
function myFunction(success)
{
setTimeout(function()
{
success('Completed', 1, true);
}, 1000);
}
function myOtherFunction(success)
{
setTimeout(function()
{
success('Completed', {a: 1, b: 2});
}, 2000);
}
new Flow(
[
{fn: myFunction},
{fn: myOtherFunction},
]).execute();
// Log:
// Completed 1 true
// Completed {a: 1, b: 2}
As we can see from the example above, after 1000 milliseconds we execute the success function with attributes we want it to receive.
Please note: Execution of the success or fail function is mandatory, otherwise the task will not finish and the flow will never be completed.
Let's see another example using jQuery to get AJAX data.
function getUserAlbums(success, fail)
{
$.ajax(
{
url: '/get-user-albums'
success: success,
fail: fail
});
}
new Flow(
[
{fn: getUserAlbums}
]).execute();
In this example we assign our success and fail functions as parameters of the $.ajax() function. As stated above, the fail function is optional.
Please note: Unless stated otherwise execution of the fail function will cause the flow to stop and trigger the onFail function.
Accepts:
new Flow()
As an alternative to executing an fn function, we can give the task a sub-flow to execute instead. If flow is defined, fn value will be ignored.
function myFunction(success)
{
setTimeout(function()
{
success('Completed', 1, true);
}, 1000);
}
function myFunctionSuccess(param1, param2, param3)
{
console.log(param1, param2, param3);
}
function subFlowSuccess()
{
console.log('Sub-flow complete!');
}
var subFlow = new Flow(
[
{fn: myFunction, success: myFunctionSuccess}
]);
new Flow(
[
{flow: subFlow, success: subFlowSuccess}
]).execute();
// Log:
// Completed 1 true
// Sub-flow complete!
Accepts:
function(attributes)
A callback function that is passed as the first parameter of the fn function of the task.
Executing it will successfully finish the task and trigger the onProgress function, unless new Error() is returned.
This function is optional (if not defined, an empty function will be passed instead) and can receive any number of parameters.
function myFunction(success)
{
setTimeout(function()
{
success('Completed', 1, true); // myFunctionSuccess() function is executed with parameters
}, 1000);
}
function myFunctionSuccess(param1, param2, param3)
{
console.log(param1, param2, param3);
}
new Flow(
[
{fn: myFunction, success: myFunctionSuccess}
]).execute();
// Log:
// Completed 1 true
Returning new Error() by the function is equal to executing the fail function.
The onProgress function will receive anything returned by success.
Accepts:
function(error)
A callback function that is passed as the second parameter of the fn function of the task.
Executing it will cause the task to fail and under default conditions fail the flow and trigger the onFail function.
This function is optional. Any result returned by this function will be passed to the onFail function.
function myFunction(success, fail)
{
setTimeout(function()
{
fail('Something went wrong!');
}, 1000);
}
function logError(error)
{
console.log(error)
}
new Flow(
[
{fn: myFunction, fail: logError}
]).execute();
// Log:
// Something went wrong!
Accepts:
booleanDefault:
falseortrueifdefaultSyncoptions is used.
When the task is set as 'sync', its execution will be delayed until all the tasks before it had completed.
function test1(success)
{
setTimeout(function()
{
success('Test1 complete');
}, 2000);
}
function test2(success)
{
setTimeout(function()
{
success('Test2 complete');
}, 1000);
}
function test3(success)
{
success('Test3 complete');
}
function logResult(result)
{
console.log(result);
}
// No sync is used
new Flow(
[
{fn: test1, success: logResult},
{fn: test2, success: logResult},
{fn: test3, success: logResult}
]).execute();
// Log:
// Test3 complete
// Test2 complete
// Test1 complete
// The last task is defined as sync
new Flow(
[
{fn: test1, success: logResult},
{fn: test2, success: logResult},
{fn: test3, success: logResult, sync: true}
]).execute();
// Log:
// Test2 complete
// Test1 complete
// Test3 complete
Accepts:
integerDefault:
0
When we want to give the task a second chance to complete successfully after failing,
we can use failRetries to define how many times we want it to retry the task.
It is important to note that the number represents a number of times to retry the task after the task has failed the first time
(i.e. setting failRetries: 2 will cause the task to execute additional two times).
var num = 0;
function myFunction(success, fail)
{
num++;
if (num % 3 > 0)
{
fail(num + ' is not divisible by 3');
}
else
{
success('Great success! ' + num + ' is divisible by 3');
}
}
function logSuccess(result)
{
console.log(result);
}
function logError(error)
{
console.log(error);
}
new Flow(
[
// Give the task another 2 attempts to succeed
{fn: myFunction, success: logSuccess, fail: logErrors, failRetries: 2}
]).execute();
// Log:
// 1 is not divisible by 3
// 2 is not divisible by 3
// Great success! 3 is divisible by 3
Accepts:
integerorfunctionDefault:
0
Used in conjunction with failRetries, we can delay the execution of another retry by a defined amount of milliseconds.
We can instead pass a function to dynamically modify the delay time (this function must return a number).
var num = 0;
function myFunction(success, fail)
{
num++;
if (num % 3 > 0)
{
fail(num + ' is not divisible by 3');
}
else
{
success('Great success! ' + num + ' is divisible by 3');
}
}
function setRetryInterval()
{
return num * 1000;
}
function logSuccess(result)
{
console.log(result);
}
function logError(error)
{
console.log(error);
}
new Flow(
[
// Dynamically set the retry interval
{fn: myFunction, success: logSuccess, fail: logErrors, failRetries: 2, retryInterval: setRetryInterval}
]).execute();
// Log:
// 1 is not divisible by 3
//
// (After 1000ms)
// 2 is not divisible by 3
//
// (After 2000ms)
// Great success! 3 is divisible by 3
Accepts:
booleanDefault:
false
Setting this as true will not cause a failed task to fail the entire flow. If 'failRetries' is used, the task will still attempt to perform retries.
function test1(success)
{
setTimeout(function()
{
success('Test1 complete');
}, 2000);
}
function test2(success, fail)
{
setTimeout(function()
{
fail('Test2 had failed!');
}, 1000);
}
function test3(success)
{
success('Test3 complete') ;
}
function logResult(result)
{
console.log(result);
}
function logError(error)
{
console.log(error);
}
// This flow will not be completed, failing on test2
new Flow(
[
{fn: test1, success: logResult},
{fn: test2, success: logResult, fail: logError},
{fn: test3, success: logResult, sync: true}
]).execute();
// Log:
// Test2 had failed!
// This flow will be completed even though test2 is set to fail
new Flow(
[
{fn: test1, success: logResult},
{fn: test2, success: logResult, fail: logError, continueOnFail: true},
{fn: test3, success: logResult, sync: true}
]).execute();
// Log:
// Test2 had failed!
// Test1 complete
// Test3 complete
Options is the second parameter you can provide to the Flow constructor to define general callbacks and certain behaviors of your flow.
new Flow([...],
{
onComplete: function()
{
console.log('Flow has completed successfully'),
},
onFail: function(error)
{
console.log('Flow has failed:', error);
},
onProgress: function(result)
{
console.log('Task returned result:', result);
}
}).execute();
| Option | Description | Accepts | Default |
|---|---|---|---|
onComplete | A callback function to be executed after each successful execution of the flow. | function | N/A |
onFail | A callback function to be executed each time the entire flow fails. If the fail function of the failed task returns a value, this value will be passed to the onFail function | function | N/A |
onProgress | A callback function that will execute each time a task completes successfully. If the success function of the task returns any value except new Error(), this value will be passed to the onProgress function. | function | N/A |
defaultSync | If set as true, defaultSync will cause all tasks to become sync by default. | boolean | false |
Methods are functions used to execute, stop or add tasks to the flow. Methods can be chained.
| Method | Description | Parameters |
|---|---|---|
execute() | Starts the execution of the flow. | Accepts two optional functions for complete and fail callbacks. |
push(tasks) | Pushes tasks to the end of the task list. | Can accept a single task object or an array of tasks. |
pushAndExecute(tasks) | A shorthand method, equals flow.push(tasks).execute(). | Accepts tasks as a first parameter and optional callbacks as second and third. |
stop() | Stops the execution of the flow and prevents the execution of callbacks of currently executing tasks. | N/A |
restart() | Restarts the execution of the flow. | Accepts two optional functions for complete and fail callbacks. |
Optional:
completeandfailcallback functions
This method starts the execution of the flow.
Please note: The optional complete and fail callbacks will execute in addition to the onComplete
and onError functions we define in the options. The main difference is that execute(complete, fail)
callbacks will execute only once, while the ones we set in the options will execute every time the flow completes or fails.
function onExecuteComplete()
{
console.log('Flow execution was completed');
}
var flow = new Flow([...],
{
onComplete: function()
{
console.log('Done and done'),
}
});
flow.execute(onExecuteComplete);
// Log:
// Done and done
// Flow execution was completed
Please note: It is not advised to use execute() on a flow that was stopped by the stop() method or by previously failing.
This is because there is no reliable way to determine the exact stopping point in the tasks list.
Use execute() only on flows that were completed and after pushing additional tasks, otherwise use restart().
Required: a task
objectorarrayof tasks
This method is used to add additional tasks to the end of the tasks list of the flow.
var flow = new Flow([...]);
flow.push({fn: test1}).push(
[
{fn: test2},
{fn: test3},
{fn: test4}
]).execute();
Required: a task
objectorarrayof tasksOptional:
completeandfailcallback functions
This is a shorthand method, equals flow.push(...).execute().
This method will stop the execution of the flow and prevent any currently executing tasks from executing their callbacks.
Optional:
completeandfailcallback functions
Use this method to restart the flow from the first task.
var loadedImages = [];
function loadImage(imageUrl, success, fail)
{
var image = new Image();
image.onload = success;
image.onerror = fail;
image.src = imageUrl;
}
function addLoadedImageToArray(event)
{
loadedImages.push(event.target); // Push the loaded image to the loadedImages array for later use
}
function imageLoader(imageUrls)
{
var tasks = [];
if (imageUrls && imageUrls.length)
{
// Create our tasks list by looping over the imageUrls array
for (var i= 0, l=imageUrls.length; i<l; i++)
{
tasks.push(
{
fn: loadImage.bind(null, imageUrls[i]), // We'll use function.bind to bind imageUrl attribute to the fn
success: addLoadedImageToArray,
continueOnFail: true // We do not want to fail the entire loading cycle if one of our images fails to load
});
}
new Flow(tasks,
{
onComplete: function(a)
{
// For the sake of this example, we'll add our loaded images to the BODY element
for (var i=0, l=loadedImages.length; i<l; i++)
{
document.body.appendChild(loadedImages[i]);
}
}
}).execute();
}
}
// Lets load some images
imageLoader(['images/1.jpg', 'images/2.jpg', 'images/3.jpg', 'images/4.jpg', 'images/5.jpg']);
Special tanks to Shane Goodson for helping me write this README and testing.
This project is licensed under the MIT License - see the LICENSE.txt file for details.
FAQs
A small JavaScript callbacks synchronizer for the browser and Node.js. A powerful alternative to using Promises, Flow.js offers you greater control, parallel execution, cleaner code and consistency across platforms.
The npm package flow-sync receives a total of 3 weekly downloads. As such, flow-sync popularity was classified as not popular.
We found that flow-sync demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.