wrkflw v1.1.3
A new type of deferred library for the browser.
Description
Workflow
is the most simple deferred library for the browser JavaScript.
This will solve the problem in a different way than before.
Browser Support
- Chrome: Current
- Edge: Current
- Firefox: Current
- Internet Explorer: 9+
- Safari: Current
- Opera: Current
Installation
Clone the repo:
$ git clone https://github.com/node-link/workflow.git
Install with npm:
$ npm install wrkflw
Install with Bower:
$ bower install workflow
And, include as follows:
<script src="workflow.min.js"></script>
Demo
Workflow
instance is "Array-like" Object.
var wf = new Workflow;
wf.push(function (next) {
console.log('It can be pushed only function');
});
console.log(wf[0]);
Adding the function to operate as one of the processing block.
By executing the callback function called next()
, to shift to the next processing.
wf.push(function (next) {
console.log('process 1');
next();
});
By calling the start()
, to run the processing blocks that you defined.
wf.start();
Synchronous processing
As follows, you can run synchronously processing.
var wf = new Workflow;
// Process1
wf.push(function (next) {
console.log('process1 start');
setTimeout(function () {
console.log('process1 is finished');
next();
}, 1000);
});
// Process2
wf.push(function (next) {
console.log('process2 start');
setTimeout(function () {
console.log('process2 is finished');
next();
}, 1000);
});
// Start Workflow
wf.start(function (start) {
// Start
console.log('start');
start();
}, function (again) {
// End
console.log('all of the processing is finished');
});
The above code will run the flow, such as:
Parallel processing
As follows, you can be the processing in parallel.
var wf = new Workflow;
// Process1
wf.push(
Workflow.parallel(
function (next) {
console.log('process1-1 start');
setTimeout(function () {
console.log('process1-1 is finished');
next();
}, 1000);
},
function (next) {
console.log('process1-2 start');
setTimeout(function () {
console.log('process1-2 is finished');
next();
}, 1000);
}
)
);
// Process2
wf.push(
Workflow.parallel(
function (next) {
console.log('process2-1 start');
setTimeout(function () {
console.log('process2-1 is finished');
next();
}, 1000);
},
function (next) {
console.log('process2-2 start');
setTimeout(function () {
console.log('process2-2 is finished');
next();
}, 1000);
},
Workflow.series(
function (next) {
console.log('process2-3-1 start');
setTimeout(function () {
console.log('process2-3-1 is finished');
next();
}, 1000);
},
function (next) {
console.log('process2-3-2 start');
setTimeout(function () {
console.log('process2-3-2 is finished');
next();
}, 1000);
}
)
)
);
// Start Workflow
wf.start(function (start) {
// Start
console.log('start');
start();
}, function (again) {
// End
console.log('all of the processing is finished');
});
The above code will run the flow, such as:
State definition
To define the state will use the set()
method. And to get the state will use the get()
method.
var wf = new Workflow;
wf.push(function (next) {
this.set('count', 0);
next();
});
wf.push(function (next) {
var count = this.get('count');
this.set('count', count + 1);
console.log(this.get('count')); // 1
next();
});
wf.push(function (next) {
setTimeout(this.f(function () {
var count = this.get('count');
this.set('count', count + 1);
console.log(this.get('count')); // 2
next();
}), 1000);
});
wf.start();
console.log(wf.get('count'));
Callback function, such as those used in such as setTimeout
is, by generating through f()
method, to hold the this
object.
If you specify an instance, it is also possible to get from the outside. In the example above, you can see if you call with wf.get('count')
.
The argument of set()
and get()
method, when you refer to an object, you can also use the dot notation.
wf.set('user', {id: 1, name: 'example'});
console.log(wf.get('user.name')); // example
To delete a state, use the unset()
method.
Watching state
By using the watch()
method, you can monitor the state. And you can run the process each time it is changed.
var wf = new Workflow;
wf.push(function (next) {
this.set('count', 0);
next();
});
wf.push(function (next) {
var count = this.get('count');
this.set('count', count + 1);
next();
});
wf.push(function (next) {
setTimeout(this.f(function () {
var count = this.get('count');
this.set('count', count + 1);
next();
}), 1000);
});
wf.watch('count', function () {
var counterElement = document.getElementById('counter');
counterElement.innerHTML = this.get('count');
});
// You can also use the Workflow.series method, the Workflow.parallel method and the wf.join method
wf.watch('count',
Workflow.series(
function (next) {
console.log('process1 start');
setTimeout(function () {
console.log('process1 is finished');
next();
}, 1000);
},
function (next) {
console.log('process2 start');
setTimeout(function () {
console.log('process2 is finished');
next();
}, 1000);
}
)
);
wf.start();
watch()
method, you can use a wildcard as follows:
wf.watch('users.*.age', function () {
var counterElement = document.getElementById('user-' + this.get('users.*.id') + '-age');
counterElement.innerHTML = this.get('users.*.age');
});
The contents of the asterisk can be referenced using the this.$
.
wf.watch('posts.*.tags.*.name', function () {
console.log(this.$[0]);
console.log(this.$[1]);
});
wf.set('posts.2.tags.4.name', 'example'); // 2 4
By using the two asterisks, you can specify that you cross the dot.
wf.watch('posts.**.name', function () {
console.log('!');
});
wf.set('posts.name', 'example'); // !
wf.set('posts.2.name', 'example'); // !
wf.set('posts.2.category.name', 'example'); // !
wf.set('posts.2.tags.4.name', 'example'); // !
To removes a watchpoint set with the watch()
method, use the unwatch()
method.
wf.watch('my.name', function () {
console.log('!');
});
wf.set('my.name', 'example'); // !
wf.unwatch('my.name');
wf.set('my.name', 'example2');
Back to the previous processing block
The second argument of the function of Workflow
is a function to return to the previous processing block.
var wf = new Workflow;
wf.push(function (next, prev, current) {
this.set('count', 0);
next();
});
wf.push(function (next, prev, current) {
var count = this.get('count');
this.set('count', count + 1);
next();
});
wf.push(function (next, prev, current) {
var count = this.get('count');
console.log(count);
if (count > 3) {
next();
} else {
prev();
}
});
wf.start(); // 1 2 3 4
And the third argument is the current function.
Process again
In the callback function of the start()
method, you can execute the processing again.
var wf = new Workflow;
wf.push(function (next, prev, current) {
var count = this.get('count');
this.set('count', count + 1);
next();
});
wf.push(function (next, prev, current) {
var count = this.get('count');
console.log(count);
next();
});
wf.start(function (start) {
this.set('count', 0);
start();
}, function (again) {
var count = this.get('count');
if (count < 10) {
again();
}
}); // 1 2 3 4 5 6 7 8 9 10
Copy the processing to another instance
By using the join()
method, it will be the processing block of an instance can be directly pushed to another instance.
var wf1 = new Workflow;
wf1.push(function (next) {
console.log('one');
next();
});
wf1.push(function (next) {
console.log('two');
next();
});
wf1.push(function (next) {
console.log('three');
next();
});
var wf2 = new Workflow;
wf2.push(wf1.join());
wf2.push(function (next) {
console.log('four');
next();
});
wf2.push(function (next) {
console.log('five');
next();
});
wf2.start(); // one two three four five
wf1.start(); // one two three
Example
In fact, it would be desirable to use in the following manner.
var wf = new Workflow;
wf.push(getMyJSON);
wf.push(checkMyUserExists);
wf.push(
Workflow.parallel(
getUserAndGroupJSON(1),
getUserAndGroupJSON(2),
getUserAndGroupJSON(3),
getUserAndGroupJSON(4),
getUserAndGroupJSON(5)
)
);
wf.watch('users.*.group.name', function () {
// rendering
var elm = document.getElementById('group-name-' + this.get('users.*.groupId'));
elm.innerHTML = this.get('users.*.group.name');
});
wf.start();
function getMyJSON(next) {
// use jQuery
jQuery.getJSON('my.json', this.f(function (json) {
this.set('my', json.my);
next();
}));
}
function checkMyUserExists(next) {
if (this.get('my.id')) {
next();
}
}
function getUserJSON(userId) { // If you want to use the argument, let's use a closure
return function (next) {
jQuery.getJSON('users/' + userId + '.json', this.f(function (json) {
this.set('users.' + userId, json.user);
next();
}));
};
}
function getGroupJSON(userId) {
return function (next) {
// require group.id
jQuery.getJSON('groups.json?' + jQuery.param({groupId: this.get('users.' + userId + '.groupId')}),
this.f(function (json) {
this.set('users.' + userId + '.group', json.group);
next();
}));
}
}
function getUserAndGroupJSON(userId) {
return Workflow.series(getUserJSON(userId), getGroupJSON(userId));
}
Since this is the only Deferred library, Rendering and XHR, etc. should be left to the other libraries.