Synchronous tasks with asynchronous task

384 views Asked by At

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>

2

There are 2 answers

2
fingerpich On BEST ANSWER

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

var doneCallback=0;
for (i = 0; i < jobs[job].length; i++) {
  tasks[i](val, function(err, result) {
    if (err) {
      errors.push(err)
    }
    doneCallback++;
    if(doneCallback==jobs[job].length)generateHtml();
  })
}

// generate the html output
function generateHtml(){
  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();
}

/**
 * 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
    var doneCallback=0;
    for (i = 0; i < jobs[job].length; i++) {
       tasks[i](val, function(err, result) {
          if (err) {
            errors.push(err)
          }
          doneCallback++;
          if(doneCallback==jobs[job].length)generateHtml();
       })
    }

    // generate the html output
    function generateHtml(){
       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>

1
Shekhar Tyagi On
the best way to complete your requirement using async .
var async = require('async');
var name = ["shekhar", 'vishal', "param", "abhishek", 'amit'];
    for (var i = 0; i < 5; i++) {
        var task_1 = name[i];//execute your first loop here
    }
    async.eachSeries(task_1, task_2, function (err) {
        if (err) {
            res.json({status: 0, msg: "OOPS! How is this possible?"});
        }
        res.json("Series Processing Done");
    })
    function task_2(item, callback) {
//execute your second loop here
        });