Description
move-on is a module that:
- executes the chosen functions (synchronous and | or asynchronous) in the chain [sample]
- can be a (really, really great) alternative for Promises
- supports timeout [sample] [sample]
- contains
four methods that immitate the Promises'
.alland.racemethods [sample] [sample] [sample] - allows to set the
thisreference inner context for all functions in the chain to transmit data between functions [sample] [sample] [sample]
Any bugs found? Give me to know on GitHub.
Usage
Node
npm install move-onjavascript
const moveOn = require('move-on');
Browsers
Add the move-on.js library to the HTML file.
The library is located in ./dist/move-on.js directory.
It is a webpack&babel bundled cross-browser library version.
The library is accessible as moveOn variable in the global (window) scope.
html
<script src='move-on.js'></script>
<script>
moveOn(list, config, onDone, onCatch);
</script>
Tests
npm test
cmd
> git clone https://github.com/devrafalko/move-on.git
> cd move-on
> npm install
> npm test //run tests in node
> npm test err //run tests in node with failed specs shown
> npm test deep //run tests in node with errors' descriptions shown
Simple sample
const moveOn = require('move-on');
/* [Function] moveOn(list, config, onDone, onCatch)
[Function] moveOn.all(list, config, onDone, onCatch)
[Function] moveOn.each(list, config, onDone, onCatch)
[Function] moveOn.first(list, config, onDone, onCatch) */
const list = [retrieveData, computeData, displayData];
const config = { timeout: 5000 };
moveOn(list, config, onDone, onCatch);
function retrieveData(resolve, reject, context){
setTimeout(resolve, 1000); //asynchronous resolve
}
function computeData(resolve, reject, context){
resolve(); //synchronous resolve
}
function displayData(resolve, reject, context){
resolve();
}
function onDone(reject, context){}
function onCatch(context){}
Methods short description
The module's methods expect the [Array]
list of functions to be passed as the first argument. Each function in the chain has the
resolve
and
reject
parameter, that should be called when ready
(or failed) in order to move the functions execution forward. When the functions chain is successfully executed, the
done
callback function is called finally, otherwise the
catch
callback function is called.
1.
moveOn
The chained functions are executed sequentially
(one after another). Each function is expected to be
resolved
, so that the next chained function was executed. The
done
function is called as the last one, when all previous chained functions resolved. The
catch
function is called instead of
done
function, when at least one chained function failed
(rejected).
See the full description below.
See the samples: [sample] [sample] [sample] [sample] [sample] [sample] [sample]
2.
moveOn.all
The
all static method of
move-on module executes all chosen functions at the same time
(similarly to Promises'
.all
method). All chained functions are expected to be
resolved
so that the final
done
function was called. The
catch
function is called instead of
done
function, when at least one chained function failed
(rejected).
See the full description below.
See the samples: [sample]
3.
moveOn.each
The
each static method of
move-on module executes all chosen functions at the same time. Each chained function is expected to be either
resolved
or
rejected
, so that the final
done
function was called. The failed
(rejected) function
does not stop the further functions execution. It can be used
eg. to log the warnings in the
catch
callback function.
See the full description below.
See the samples: [sample]
4.
moveOn.first
The
first static method of
move-on module executes all chained functions at the same time. It expects the first
(fastest) function to be
resolved
, so that the
done
function was called
(similarly to Promises'
.race
method). When
all functions failed
(rejected), the
catch
function is called instead.
See the full description below.
See the samples: [sample]
Methods behaviour
moveOn(
list
,
config
,
done
,
catch
)
- The
move-onmodule function executes thelistfunctions sequentially (one after another) in the chain - When one
listfunction resolves , the nextlistfunction is called, and so on... - When the
last
listfunction resolves , thedonefunction is called once (it ends up the module execution) - When
whichever
listfunction rejects , the fartherlistfunctions and thedonefunction are not called in the end - When
whichever
listfunction rejects , thecatchfunction is called instead once (it ends up the module execution) - Each
listfunction can be resolved and | or rejected multiple times. The forks of chain are created and executed then [read more] - Each
listfunction can execute the innermove-onmodule [read more] - Samples: [sample] [sample] [sample] [sample] [sample] [sample] [sample]
moveOn.all(
list
,
config
,
done
,
catch
)
-
move-on.allstatic method executes all thelistfunctions simultaneously (at the same time) - When one
listfunction resolves , thedoneis not called immediately - The
donewaits, till alllistfunctions are resolved , to be called (it ends up the module execution - after that, all resolve and reject calls are ignored) - When
whichever
listfunction rejects , thedonefunction is not called in the end - When
whichever
listfunction rejects , thecatchfunction is called instead once (it ends up the module execution - after that, all resolve and reject calls are ignored) - When
whichever
listfunction resolves and | or rejects multiple times, only the first call is respected [read more] - Each
listfunction can execute the innermove-onmodule [read more] - Samples: [sample]
moveOn.each(
list
,
config
,
done
,
catch
)
-
move-on.eachstatic method executes all thelistfunctions simultaneously (at the same time) - When one
listfunction resolves , thedoneis not called immediately - The
donewaits, till eachlistfunction is either resolved or rejected , to be called (it ends up the module execution - after that, all resolve and reject calls are ignored) - When
whichever
listfunction rejects and thecatchis called, it does not end up the module execution - When
whichever
listfunction resolves and | or rejects multiple times, only the first call is respected [read more] - Each
listfunction can execute the innermove-onmodule [read more] - Samples: [sample]
moveOn.first(
list
,
config
,
done
,
catch
)
-
move-on.firststatic method executes all thelistfunctions simultaneously (at the same time) - The
donewaits, till the first (fastest)listfunction is resolved , to be called (it ends up the module execution - after that, all resolve and reject calls are ignored) - When
all
listfunctions reject , thedonefunction is not called in the end - When
all
listfunctions reject , thecatchfunction is called instead once (it ends up the module execution - after that, all resolve and reject calls are ignored) - When
whichever
listfunction resolves and | or rejects multiple times, only the first call is respected [read more] - Each
listfunction can execute the innermove-onmodule [read more] - Samples: [sample]
Arguments
list
[Array: function | array]
The [Array]
list stores the list of functions, that should be called. It can contain:
- [Function] items
[see below] [sample] [sample] [sample] [sample]
const list = [fnA, fnB, fnC]; - or [Array] items that store the [Function] items
[see below]
const list = [fnA, [obj, fnB, fnC], fnD] - or [Array] items that store the [String] names of methods
[see below] [sample]
const list = [fnA, [obj, 'fnB', 'fnC'], fnD]
1. [Function] items
- The [Array]
listcan contain [Function] items. It may be function, arrow function or object's method - All functions are
bound by default to the
config.contextreference (except arrow functions and already bound functions [read more]) - Samples: [sample] [sample] [sample] [sample]
javascript
const retrieveData = function(){};
const computeData = ()=>{};
const displayData = { display:()=>{} };
const list = [retrieveData, computeData, displayData.display];
2. [Array: function] items for individual binding
- All chained functions are
bound by default to the
config.contextreference - You can set the
individual
thisreference for the chosen functions (except arrow functions and already bound functions [read more]) - In order to bind the chained functions individually, push [Array] item into the
list:- The
[0]item should indicate the object or value to be thethisreference for the functions - The
[1],[2], etc... item(s) should indicate the function(s), that will be bound to the[0]object or value
- The
- The [Array] item's functions are bound to the given
[0]object or value instead of theconfig.context - The
config.bindsetting does not affect the individualthisreference setting - The [Array] item's functions still have the access to the
config.contextparameter - the
listcan still contain the [Function] items next to this [Array] item
javascript
const workers = {}, earnings = {}, tasks = {};
const config = {context: tasks}; //the default this reference
const list = [
functionA, //this === tasks
[workers, functionB], //this === workers
[earnings, functionC] //this === earnings
];
moveOn(list, config, onDone, onCatch));
3. [Array: string] items for methods
The methods passed to thelist
loses their
this reference to the object, they were declared in, what may be undesirable.
javascript
const workers = {
addWorker: function(){},
listEarnings: function(){}
};
const list = [
workers.addWorker, //this !== workers
workers.listEarnings //this !== workers
];
- to retain the
thisreference to the object, that the methods are declared in, push [Array] item with methods' [String] names into thelist:- The
[0]item should indicate the object, that the methods are declared in - The
[1],[2], etc... item(s) should indicate the [String] name(s) of the method(s) declared in the[0]object
- The
- These methods retain the
thisreference to the[0]object and are not bound to theconfig.context - The
config.bindsetting does not affect thethisreference - The [Array] item functions still have the access to the
config.contextparameter - the
listcan still contain the [Function] items or [Array] items with functions next to this [Array] item with [String] method's names - Samples: [sample]
javascript
const displayData = function(){};
const workers = {
addWorker: function(){},
listEarnings: function(){}
};
const list = [ [workers, 'addWorker', 'listEarnings'], displayData ];
moveOn(list, config, onDone, onCatch));
config
[Object | null]
- the [Object]
configargument allows to set the following config properties:timeout,bind,context,passContext - when the
configis set tonullor when it does not define the particular config property or when it defines the config property incorrectly, the default value is used for this config property instead [sample] [sample] [sample] - any error is thrown when any config property is defined incorrectly (the default value is used instead)
config.timeout
Type: [Number | null | Infinity]Default:
10000
Description:
- It must be a [Number] integer, equal or bigger than
0, that indicates the milliseconds - it behaves different for each method:
-
moveOn:
Theconfig.timeoutstarts out counting down individually for each chained function immediately after it is called.
It expects each function to be resolved or rejected before timeout pass,
otherwise it calls thecatchfunction with the timeout error argument passed -
moveOn.all:
Theconfig.timeoutstarts out counting down once for all chained functions when the module is fired.
It expects all functions to be resolved or any function to be rejected before timeout pass,
otherwise it calls thecatchfunction with the timeout error argument passed -
moveOn.each:
Theconfig.timeoutstarts out counting down once for all chained functions when the module is fired.
It expects all functions to be either resolved or rejected before timeout pass,
otherwise it calls thecatchfunction with the timeout error argument passed -
moveOn.first:
Theconfig.timeoutstarts out counting down once for all chained functions when the module is fired.
It expects at least one function to be resolved or all functions to be rejected before timeout pass,
otherwise it calls thecatchfunction with the timeout error argument passed
-
- All
resolvess andrejects that are called after theconfig.timeoutpass are ignored - When the
config.timeoutis set tonullorInfinity, the timeout is not set at all. If any of the chained function does not resolve (or reject), anything happen then and thedoneorcatchfunction is never called in the end - When the
config.timeoutis not defined, or if it is defined with incorrect value, the default value is set instead - Samples: [sample] [sample]
Timeout error
It is an [Error] object with the following properties, that allow to distinguish, that the timeout error has been passed:-
message: eg."Timeout. The chained function did not respond in the expected time of 10000 ms." -
info:"timeout" -
code:"ETIMEDOUT"
config.context
Type: [any]
Default:
{}
Description:
- The
config.contextrefers to the object (or value), that will be used as thethisreference in alllistfunctions,doneandcatch - It is usefull to transmit data between functions; eg. the [Object]
config.context's properties can be defined and got in any function - The
config.contextcan be any value, as any value can be used as thethisreference inFunction.prototype.bind[read more] - The
config.contextis used as thethisreference by default, unless you setconfig.bindto false - The
config.contextis also accessible as the parameter, unless you setconfig.passContextto false - Samples: [sample] [sample] [sample]
config.passContext
Type: [Boolean]
Default:
true
Description:
- By default, the
config.contextobject (or value) is passed through eachlistfunction, thedoneandcatchas the argument: - In order not to pass the
config.contextas the argument, setconfig.passContexttofalse - The
config.contextaccessible as the parameter is usefull:- if the
listfunctions,doneorcatchare arrow functions, that are non-binding and cannot refer to theconfig.contextviathiskeyword - if you compose the
listwith individually bound functions or methods names , that do not refer to theconfig.contextviathiskeyword - if
listfunctions,doneorcatchare already bound
- if the
- Samples: [sample] [sample]
config.bind
Type: [Boolean]
Default:
true
Description:
- By default, each
listfunction,doneandcatchare bound to theconfig.contextobject (or value), thus thethiskeyword refers to theconfig.context - In order to retain the former
thisreference of all functions, set theconfig.bindtofalse - In order to set the individual
thisreference for chosen functions, see thelistconstructing options -
keep in mind, that arrow functions are non-binding and that already bound functions cannot have the
thisreference changed anymore
done(
reject
,
context
)
[Function]
The
done is a callback function, that
(in general) is called as the last one, when the
list
functions have been successfully executed. The
done is called in a different way and time, depending on which method is called:
-
moveOnThedoneis called, when the last function from thelistcollection is resolved.
The arguments passed throughdone:
[0]reject
[1]config.context
[2],[3], etc... The arguments passed by the last resolvedlistfunction -
moveOn.allThedoneis called, when alllistfunctions are resolved.
The arguments passed throughdone:
[0]reject
[1]config.context
[2]resolveMap -
moveOn.eachThedoneis called, when alllistfunctions are either resolved or rejected.
The arguments passed throughdone:
[0]reject
[1]config.context
[2]resolveMap -
moveOn.firstThedoneis called, when the first (fastest)listfunction is resolved.
The arguments passed throughdone:
[0]reject
[1]config.context
[2],[3], etc... The arguments passed by the first (fastest) resolvedlistfunction
resolveMap object
- The
resolveMapobject is passed throughdonecallback when themoveOn.allandmoveOn.eachmethod is executed. It stores all arguments that have been passed by eachlistfunction'sresolvecall. - The
resolveMapcontains allargumentsobjects at the indeces that correspond to the order oflistfunctions calling; the thirdlistfunction's arguments are accessible viaresolveMap[2], and so on... - The
resolveMapproperties:-
missingIt returns the [Array] list of thoselistfunctions' indeces (due to the order of calling) that have not been resolved
-
- The
resolveMapmethods:-
forEach
It loops through eachargumentsobject.
It expects the[0]parameter to be the [Function] callback.
The [Function] callback is called for eachargumentsobject.
The callback parameters:{0: arguments, 1: argumentsIndex, 2: resolveMap}
Usage:resolveMap.forEach((arguments, argumentsIndex, resolveMap) => {} ); -
forAll
It loops through each item (argument) of eachargumentsobject.
It expects the[0]parameter to be the [Function] callback.
The [Function] callback is called for each item (argument).
The callback parameters:{0: argument, 1: argumentsIndex, 2: itemIndex, 3: resolveMap}
Usage:resolveMap.forAll((argument, argumentsIndex, itemIndex, resolveMap) => {} );
-
- Samples: [sample] [sample]
catch(
context
)
[Function]
The
catch is a callback function, that
(in general) is called as the last one, when the
list
function(s) have failed. The
catch is called in a different way and time, depending on which method is called:
-
moveOnThecatchis called, when anylistfunction rejects.
The arguments passed throughcatch:
[0]config.context
[1],[2], etc... The arguments passed by the rejectedlistfunction -
moveOn.allThecatchis called, when anylistfunction rejects.
The arguments passed throughcatch:
[0]config.context
[1],[2], etc... The arguments passed by the rejectedlistfunction -
moveOn.eachThecatchis called for eachlistfunction rejection.
The arguments passed throughcatch:
[0]config.context
[1],[2], etc... The arguments passed by the rejectedlistfunction -
moveOn.firstThecatchis called, when alllistfunction rejected.
The arguments passed throughcatch:
[0]config.context
[1]rejectMap
rejectMap object
- The
rejectMapobject is passed throughcatchcallback when themoveOn.firstmethod is executed. It stores all arguments that have been passed by alllistfunctions'rejectcalls - The
rejectMapcontains allargumentsobjects at the indeces that correspond to the order oflistfunctions calling; the thirdlistfunction's arguments are accessible viarejectMap[2], and so on... - The
rejectMapmethods:-
forEach
It loops through eachargumentsobject.
It expects the[0]parameter to be the [Function] callback.
The [Function] callback is called for eachargumentsobject.
The callback parameters:{0: arguments, 1: argumentsIndex, 2: rejectMap}
Usage:rejectMap.forEach((arguments, argumentsIndex, rejectMap) => {} ); -
forAll
It loops through each item (argument) of eachargumentsobject.
It expects the[0]parameter to be the [Function] callback.
The [Function] callback is called for each item (argument).
The callback parameters:{0: argument, 1: argumentsIndex, 2: itemIndex, 3: rejectMap}
Usage:rejectMap.forAll((argument, argumentsIndex, itemIndex, rejectMap) => {} );
-
Chained functions
- Each
listfunction is called with the following arguments passed:-
[0]resolvecallback function -
[1]rejectcallback function -
[2]config.contextobject (or value) -
[3],[4], etc... (formoveOnmethod only) The arguments passed by the previouslistfunction
-
- Both
resolveandrejectcan be called with any number of arguments [sample] [sample] [sample] [sample] - When the
resolveis called with arguments, these arguments will be passed:-
moveOn: for the furtherlistfunction (or for thedonefunction, when the lastlistfunction resolves) -
moveOn.first: for thedonefunction -
moveOn.all,moveOn.each: for thedonefunction in theresolveMapobject
-
- When the
rejectis called with arguments, these arguments will be passed:-
moveOn,moveOn.all,moveOn.each: for thecatchfunction -
moveOn.first: for thecatchfunction in therejectMapobject
-
javascript
function fetchData(resolve, reject, context){
this.someAsyncAjaxHere((err, data) => {
if(err) return reject(new Error('Could not read the data.'));
this.data = data;
return resolve();
});
}
Multiple
resolve
|
reject
calls
-
keep in mind that both
resolveandrejectdo not end function execution. In order to end function execution, usereturn resolve();orreturn reject(); - the
moveOn.all,moveOn.eachandmoveOn.firstmethods expect thelistfunctions to callresolveorrejectonce - the
moveOnmethod, as it calls thelistfunctions sequentially, accepts the multipleresolveandrejectcalls:- when the
listfunction calls theresolvetwice, it runs the furtherlistfunctions twice (the forks are created); theresolvecan be called eg. with different arguments - when the
listfunction calls therejecttwice, it calls thecatchtwice; therejectcan be called eg. with different [Error] objects - when the
listfunction calls bothresolveandreject, it both runs the furtherlistfunctions and calls thecatch
- when the
- Samples: [sample] [sample]
inner
move-on module
- the
listfunction can also contain the innermove-onmodule execution, that has thedoneargument set to theresolvecallback of thislistfunction - Samples: [sample] [sample]
Code examples
1. The move-on chain of synchronous and asynchronous functions
const moveOn = require('move-on');
const list = [requestData, parseData, displayData];
const config = {
context:{
table: document.getElementById('list') //accessible in all functions as this.table
}
};
moveOn(list, config, onDone, onCatch);
//asynchronous
function requestData(resolve, reject, context) {
getAjaxData((err, json) => {
if (err) return reject(err);
this.json = json;
resolve();
});
}
//synchronous
function parseData(resolve, reject, context) {
this.data = parseJSON(this.json);
this.employees = getEmployeesList(this.data);
this.earnings = getEarningsList(this.data);
resolve();
}
function displayData(resolve, reject, context) {
table.innerHTML = parseTableContent(this.employees, this.earnings);
resolve();
}
function onDone(reject, context) {
this.table.style.display = "table";
}
function onCatch(context, err) {
throw new Error(`Could not get the data: ${err}`);
}
2. The move-on module with the user config.context and the arguments passed through the resolve and reject arrow callback functions
const moveOn = require('move-on');
function check(resolve, reject) {
console.log(this.name); //Jessica
console.log(this.age); //25
//the [String] argument passed through the catch callback function
if (!this.name || !this.age) return reject('The person was not defined.');
return resolve();
}
const config = {
context: {
name: 'Jessica',
age: 25
}
};
moveOn([check], config, (reject, context) => {
//the arrow function could not be bound to the context reference
//but the context is still accessible as the parameter
console.log(`New person added: ${context.name} (${context.age})yo.`);
}, (context, err) => {
console.error(err); //The [String] argument passed through the reject callback function
});
3. The move-on module that rejects
Mind that the second rejected function ends up the execution of further chained functions.
const moveOn = require('move-on');
moveOn([a, b, c], null, onDone, onCatch);
function a(resolve, reject) {
resolve();
}
function b(resolve, reject) {
reject('oops!');
}
function c(resolve, reject) {
//it's been never called!
resolve();
}
function onDone(reject, context) {
//it's been never called!
}
function onCatch(context, message) {
console.log(message); //oops!
}
4. The move-on instructions after resolve call
In order to end up the chained function's execution, call return resolve();
const moveOn = require('move-on');
moveOn([a, b, c], null, onDone, onCatch);
/* logs order:
A before
B before
C before
Done!
C after
B after
A after
*/
function a(resolve) {
console.log('A before');
resolve();
console.log('A after');
}
function b(resolve) {
console.log('B before');
resolve();
console.log('B after');
}
function c(resolve) {
console.log('C before');
resolve();
console.log('C after');
}
function onDone(reject, context) {
console.log('Done!');
}
function onCatch(context, msg) {
console.log(msg); //oops!
}
5. The inner move-on module and multiple resolve and reject calls
Mind how X, Y and Z functions of the inner module execute between A, B and C chained functions.
Mind how the B and C chained functions are executed twice, by doubleresolvecall in A chained function.
const moveOn = require('move-on');
moveOn([a, b, c], null, onDone, onCatch);
/* The order of functions execution:
A, X, Y, Z, B, C, Done, Catch, B, C, Done, Catch */
function a(resolve, reject) {
console.log('A');
moveOn([x, y, z], null, () => resolve(), () => reject);
resolve();
}
function b(resolve) {
console.log('B');
resolve();
}
function c(resolve, reject) {
console.log('C');
resolve();
reject();
}
function x(resolve) {
console.log('X');
resolve();
}
function y(resolve) {
console.log('Y');
resolve();
}
function z(resolve) {
console.log('Z');
resolve();
}
function onDone() {
console.log('Done');
}
function onCatch() {
console.log('Catch');
}
6. The move-on.all chain
It callsdonecallback function right after all chained functions are resolved.
The user's shorterconfig.timeoutis set.
Therejectcallback functions are not used. In case of error, thecatchcallback function will still be called withconfig.timeouterror.
All retrieved data is passed through theresolvecallback and accessible in theResolveMapdonecallback function.
const moveOn = require('move-on');
const list = [getArticle, getTagList, getCommentSection];
moveOn.all(list, { timeout: 5000, passContext: false }, onDone, onCatch);
function getArticle(resolve) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) return resolve(xhr.responseText);
};
xhr.open("GET", "article.js", true);
xhr.send();
}
function getTagList(resolve) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) return resolve(xhr.responseText);
};
xhr.open("GET", "tags.js", true);
xhr.send();
}
function getCommentSection(resolve) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) return resolve(xhr.responseText);
};
xhr.open("GET", "comments.js", true);
xhr.send();
}
function onDone(reject, context, map) {
let article = JSON.parse(map[0][0]);
let tags = JSON.parse(map[1][0]);
let comments = JSON.parse(map[2][0]);
}
function onCatch(err) {
throw new Error(err);
}
7. The move-on.each inner module
Themove-onis used to get the files list asynchronously and then to copy all files asynchronously.
The innermove-on.eachmodule is injected in the second chained function in order to report the success | failure message for each copied file.
Each copying failure occurrence callsrejectcallback and logs the warning.
When all files are (un)successfully copied, thedonecallback function is called, that indicates the end of files copying action.
const moveOn = require('move-on');
const fs = require('fs');
const path = require('path');
const list = [getContentsList, copyFiles];
const config = {
passContext: false,
context: {
copyFrom: './modules',
copyTo: './prod/modules'
}
};
moveOn(list, config, onDone, onCatch);
function getContentsList(resolve, reject) {
fs.readdir(this.copyFrom, (err, files) => {
if (err) return reject(`Could not get the access to the "${this.copyFrom}" path.`);
resolve(files); //the files object will be passed through the second function
});
}
function copyFiles(resolve, reject, files) {
const list = [];
//the moveOn.each will take the same user context to get the access to the paths
const config = { context: this, passContext: false };
//creating the list of chained functions for each files item:
for (let file of files) list.push((resolve, reject) => {
let fromPath = path.resolve(this.copyFrom, file);
let toPath = path.resolve(this.copyTo, file);
fs.copyFile(fromPath, toPath, (err) => {
//the reject call does not abort the module execution in moveOn.each method
if (err) return reject(`The file "${file}" could not be copied.`);
resolve(file); //the file path is added to the [Resolved] map, accessible in the final done callback function
});
});
//the inner moveOn.each module - as the done callback - calls the resolve callback of the second copyFiles function with [Resolved] map argument
moveOn.each(list, config, (reject, map) => resolve(map), (err) => console.warn(err));
}
function onDone(reject, map) {
//the [Resolved] map contains the collection of all passed files paths
//the missing property contains the indeces of all moveOn.each chained functions that have been rejected
let message = !map.missing.length ? 'All files have been successfully moved.' : 'The files have been moved with some errors.';
console.log(message);
}
function onCatch(err) {
throw new Error(err);
}