I am putting together a validation component on an application which takes a task name (which would be a field name) and a value as it's parameters. For each found form field a task is launched.
If the validation fails then an error is returned, else the input is fine and the next field task is launched until all fields are processed.
With the example below, task_1
works as expected. However, in a case where a HTTP request may be required (simulated by setTimeout) then task_2
does not return the desired result.
I am sure that this is something to do with asynchronicity and perhaps there is a better way to implement such a mechanism. This is actually going to be implemented on a NodeJS backend where the async
library is available, I would accept a solution which uses Async.
Please see this Fiddle for the below example. Or this Fiddle for an Async implementation.
/**
* Selecting task_1 below, will have the desired results,
* however if you run task_2 the final task will not work?
* A setTimeout() has been used to simulate a HTTP request etc..
*/
// output element
var $output = $$('output')
// configuration object, each task has an
// array of functions which must accept a
// value which would be validated
var jobs = {
task_1: [
function(val, cb) {
cb(null, val)
},
function(val, cb) {
cb('Error', null)
}
],
task_2: [
function(val, cb) {
cb(null, val)
},
function(val, cb) {
setTimeout(function() {
cb('An ajax request has an error', null)
}, 2000)
}
]
}
// run a task and pass a value
taskRunner('task_2', 'Karl')
// taskRunner
function taskRunner(job, val) {
var tasks = jobs[job]
var errors = []
if (tasks) {
// run the found tasks
for (i = 0; i < jobs[job].length; i++) {
tasks[i](val, function(err, result) {
if (err) {
errors.push(err)
}
})
}
// generate the html output
var html = errors.length > 0 ? [
'<strong class="text-danger">',
'Failure</strong>',
'<pre>',
errors,
'</pre>'
].join('') : [
'<strong class="text-success">',
'Success</strong>',
'<pre>All validations passed</pre>'
].join('')
$output.html(html).fadeIn()
} else {
// return an error message if no tasks have been found
$output.html('<span class="text-danger">There are no tasks for ' + job + '.</span>').fadeIn()
}
}
// select elements by data attribute
function $$(val) {
return $('[data-' + val + ']')
}
/* default */
html,
body {
margin: 0;
padding: 0;
font-family: "Lucida Console", Monaco, monospace !important;
background: whitesmoke;
color: #333;
}
/* output container */
.output {
max-width: 75%;
margin: 5% auto;
padding: 15px;
border: 1px solid #ddd;
border-radius: 3px;
background: white;
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
}
/* formatted output */
pre {
background: #FAFAFA;
padding: 15px;
margin-bottom: 0;
}
/* type styles */
.text-muted {
opacity: .5
}
.text-danger {
color: #D00
}
.text-success {
color: #0D0
}
/* hide if empty */
.js-hide {
display: none
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="output js-hide" data-output></div>
you should generate the html output after all callback done
for this purpose with promise you can use promise.all
but for callback you can use this code