Background Tasks

Background Tasks

Background tasks are async functions that do not require awaiting before proceeding to subsequent tasks in the queue, but still require adherence to an Error Handling Rule (discussed in Error Handling section of The Queue). To get a better understanding of this concept, consider the following functions.

function multiply(a, b, c, obj) { // public function
  if (!a) throw new Error(`"a" must be a number greater than zero, but received: ${a}`);
  if (!b) return;
  return multiplier(a, b, c, obj)
}
function multiplier(a, b, c, obj) { // private function
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (obj && obj.reject) reject(new Error('Example error after timeout'));
      else resolve({ m1: 10 * a, m2: 20 * b, m3: 30 * (c || 0) });
    }, 10);
  });
}

Now lets consider the different results when invoking multiply.

try {
  // caught since error is thrown immediately
  multiply(0);
  // .catch() is required since a promise is returned
  multiply(1, 2, 3, { reject: true }).catch((err) => {
    console.error(err);
  });
  // TypeError: Cannot read property 'catch' of undefined
  multiply(1, 0, 3, { reject: true }).catch((err) => {
    console.error(err);
  });
} catch (err) {
  console.error(err);
}

As illustrated, there are quite a few ways that an error can be either intentionally or unintentionally thrown. Another option would be to convert function multiply to async function multiply, but that would add another Promise wrapper that would need to be processed internally during the next iteration of the Event Loop. And that's assuming the function doesn't belong to an external module where we do not have any control over it's implementation. With all the different variations involved, it's better to keep it simple and mitigate any risk by wrapping background tasks within another async function. Fortunately, asynchro will do this for us everytime we call Asynchro.background (unless always throwing: throws = true). Take for instance the subsequent example:

const ax = new Asynchro({}, false, console.log);
ax.series('one', multiply, 1, 2, 3); // multiply from previous examples
ax.background('two', multiply, 4, 5, 6);
ax.background('three', multiply, 0);
ax.background('four', multiply, 1, 2, 3, { reject: true });
ax.background('five', multiply, 1, 0, 3, { reject: true });
await ax.run();
// { one: { m1: 10, m2: 40, m3: 90 } }
console.log(ax.result);
// [
//  { Error: "a" must be a number greater than zero, but received: 0
//    ...
//    Asynchro: {
//      name: 'three',
//      operation: 'multiply'
//      isPending: false,
//      isParallel: false,
//      isBackground: true
//    }
//  }
// ]
console.log(ax.errors);

Only the result for one is set in Asynchro.result. And since the only error that occurred was the one that was immediately thrown when multiply(0) was called, it is the only one recorded yet in Asynchro.errors. If it is desirable to capture background results/errors that occurred after all the background tasks have been awaited for (either completed or caught errors) Asynchro.backgroundWaiter can be awaited for after awaiting Asynchro.run.

const ax = new Asynchro({}, false, console.log);
ax.series('one', multiply, 1, 2, 3); // multiply from previous examples
ax.background('two', multiply, 4, 5, 6);
ax.background('three', multiply, 0);
ax.background('four', multiply, 1, 2, 3, { reject: true });
ax.background('five', multiply, 1, 0, 3, { reject: true });
await ax.run();
// could also pass in a different result object as the 1st argument
// NOTE: awlays use return Asynchro instance in case branching took place
const abx = await ax.backgroundWaiter();
// {
//  one: { m1: 10, m2: 40, m3: 90 },
//  two: { m1: 40, m2: 100, m3: 180 },
//  three: undefined,
//  four: undefined,
//  five: undefined
// }
console.log(abx.result);
// [
//  { Error: "a" must be a number greater than zero, but received: 0
//    ...
//    Asynchro: {
//      name: 'three',
//      operation: 'multiply'
//      isPending: false,
//      isParallel: false,
//      isBackground: true
//    }
//  },
//  { Error: Example error after timeout
//    ...
//    Asynchro: {
//      name: 'four',
//      operation: 'multiply'
//      isPending: false,
//      isParallel: false,
//      isBackground: true
//    }
//  },
// ]
console.log(abx.errors);

NOTE: It's important to keep in mind that any Asynchro.errors that are caught while invoking/awaiting Asynchro.background tasks may continue to be added long after Asynchro.run is awaited for. Background results are only set on Asynchro.result when Asynchro.backgroundWaiter is called (alternatively, a different result object could be specified during invocation).

Verification >>


4.0.0 (2019-12-20)

Full Changelog

Features: