Class in Sqimitive Async

class Sqimitive.Async extends Sqimitive.Base
Asynchronous operation manager – a “promise” on steroids

Defined in: sqimitive-async.js, line 43

Description (skip)

Asynchronous operation manager – a “promise” on steroids.

Async is “native” Sqimitive’s re-implementation of JavaScript’s Promise. It provides a unified wrapper around asynchronous operations such as remote requests. Unlike Promise, Async may contain nest’ed operations (the parent only completes when all children complete, recursively), allows the usual (evt) event listeners and can be abort()ed.

Async’s state is stored in the status _opt’ion (true when succeeded, false if failed, null if still incomplete). On change, Async fires success or error and, always, complete:

Other notes:

ExampleA subclass that wraps around JavaScript’s Image (representing the <img> tag) for preloading graphical resources.
var AsyncImage = Sqimitive.Async.extend({
  _opt: {
    src: null,   // relative to current location
    img: null,   // JavaScript's Image
  },

  events: {
    init: function () {
      var img = new Image
      img.onload  = () => this.set('status', true)
      img.onerror = () => this.set('status', false)
      this.set('img', img)
      img.src = this.get('src')
    },

    // Stops image loading (if supported by the browser).
    abort: function () {
      this.img() && (this.img().src = 'javascript:void 0')
    },
  },

  img: function () {
    return this.get('img')
  },

  width: function () {
    return this.img() && this.img().width
  },

  height: function () {
    return this.img() && this.img().height
  },
})
Using AsyncImage to load a single image:
;(new AsyncImage({src: 'pic.jpg'})
  .whenSuccess(async => alert(async.width()))

// Similar to:
var img = new Image
img.src = 'pic.jpg'
img.onload = () => alert(img.width)
Loading multiple AsyncImage’s:
var container = new Sqimitive.Async
container.nest(new AsyncImage({src: 'foo.png'}))
container.nest(new AsyncImage({src: 'bar.bmp'}))
container.whenSuccess(function () {
  alert('Loaded ' + container.invoke('get', 'src').join())
})
container.whenError(function () {
  // Since Async is the usual Sqimitive.Base, we have all the utility
  // methods:
  var failed = this.reject(a => a.isSuccessful())
  alert('Failed to load: ' + failed.map(a => a.get('src')).join())
})
The above container in turn could be part of another Async:
var resourcePreloader = new Sqimitive.Async
resourcePreloader.nest(imagePreloader)
resourcePreloader.nest(dataPreloader)
resourcePreloader.whenSuccess(() => game.start())
…With dataPreloader defined “ad-hoc” (without a specialized class):
var dataPreloader = new Sqimitive.Async

$.loadJSON('map.json', function (data) {
  dataPreloader.set('data', data)
  dataPreloader.set('status', true)
})

dataPreloader.whenSuccess(function () {
  var data = dataPreloader.get('data')
  // ...
})

Constants

MAX_PRIORITY

The maximum number (absolute, i.e. positive) accepted by whenComplete() and others; out of range priority is clamped to the nearest value.

ExampleUse -Infinity and Infinity to specify minimum and maximum priority.
// This function is called before listeners to 'success' with a larger
// priority (which is 0 by default).
async.whenSuccess(function (async) { ... }, this, -Infinity)

Defined in: sqimitive-async.js, line 188Show code

MAX_PRIORITY: 2,

Properties

_opt

Modifiers: protected

New standard options (Base._opt):

Members
Name Types Notes
statusoperation null not started or not finished Reflects both this instance’s own status and status of its children.

When an operation wrapped by this Async finishes and assuming it has no _children, set() status to either true (isSuccessful) or false. This instance will stop being isLoading(); this should be a permanent condition – don’t change status once it’s becomes non-null until clear().

true operation succeeded
false operation failed
ignoreErrorbool If set, this instance will be considered succeeded by isSuccessful() even if status is false

Don’t change this value when this instance is no longer isLoading() because the expected handlers won’t be called:

async.set('status', false)
async.whenSuccess(...)
async.set('ignoreError', true)
  // whenSuccess' func is not executed despire isSuccessful()
  // returning true

// Best practice is to give ignoreError to the constructor:
var async = new Sqimitive.Async({ignoreError: true})
// ...or to extend():
var MyAsync = Sqimitive.Async.extend({
  _opt: {ignoreError: true},
})

Defined in: sqimitive-async.js, lines 223-226 (4 lines) • Show code

_opt: {
  status: null,
  ignoreError: false,
},

Methods

abort ( )

Aborts the operation of this instance.

Exampleabort() doesn’t affect own _children – call sink() to abort everything recursively:
async.sink('abort')

The default implementation does nothing (is a stub).

Defined in: sqimitive-async.js, line 607

clear ( )

Prepares this instance for new use.

It’s recommended to create a new Async instance for every new batch of tasks. However, clear() may improve performance when thousands of objects need to be created but some could be reused.

Removes event listeners for success/error/complete (but not for exception), calls abort() on self and on _children (non-recursively), calls Base.remove() on children and sets status (_opt) to null (isLoading()).

Defined in: sqimitive-async.js, lines 469-476 (8 lines) • Show code

clear: function () {
  _.each(['success', 'error', 'complete'], function (e) { this.off(e) }, this)
  this.abort()
  this.invoke('abort')
  this.invoke('remove')
  this.set('status', null)
  return this
},
complete ( this )

Called when this instance stops being isLoading().

from sec

success, error and complete are called when this and nested operations have finished (resources loaded, etc.). Consider this analogy with exception handling:

try {
  ...                    // Async.success()
} catch (exception) {
  ...                    // Async.error()
} finally {
  ...                    // Async.complete()
}

But note that “real” exceptions thrown by listeners are handled by exception() outside of the normal pipeline (error’s are expected conditions, exceptions are not).

Each handler is called at most once as if it was bound with once(). If a handler changes the status _opt, others are skipped and events are re-fired.

ExampleIn most cases you need to use whenSuccess(), whenError() and whenComplete() rather than subscribing via on() because if the instance’s status changes between its construction and your subscription – your handler will not be called:
var async = new MyAsync({...})
// If async is already complete before we call on() - our handler will
// not be triggered:
async.on('success', function (async) { ... })
// So you want to do this:
async.whenSuccess(function (async) { ... })

The call order of handlers is deterministic (Async’s nest() acts as on() because of _childEvents/_forward()) but it’s usually more convenient to use specific priority levels to not depend on the order of nest()/on() calls: on('success') is guaranteed to be called after success-3 but before success2.

Defined in: sqimitive-async.js, line 562

doneIfEmpty ( )

Sets the status _opt to true if have no _children and is still isLoading().

Result Types
Types Notes
undefined if not empty or is not loading
true
Example
_.each(filesToLoad, function (url) {
  async.nest(new MyAsync({src: url}))
})

async.whenComplete(allLoaded)

async.doneIfEmpty()
  //=> true
  // if there were no filesToLoad then mark async as complete right
  // away

async.doneIfEmpty()
  //=> undefined - already complete, call ignored

Defined in: sqimitive-async.js, lines 452-457 (6 lines) • Show code

doneIfEmpty: function () {
  if (!this.length && this.isLoading()) {
    this.set('status', true)
    return true
  }
},
error ( this )

Called when this instance or one of its children has failed.

By default, Async doesn’t store any error information because it is operation-agnostic; you may use _opt for this.

from sec

success, error and complete are called when this and nested operations have finished (resources loaded, etc.). Consider this analogy with exception handling:

try {
  ...                    // Async.success()
} catch (exception) {
  ...                    // Async.error()
} finally {
  ...                    // Async.complete()
}

But note that “real” exceptions thrown by listeners are handled by exception() outside of the normal pipeline (error’s are expected conditions, exceptions are not).

Each handler is called at most once as if it was bound with once(). If a handler changes the status _opt, others are skipped and events are re-fired.

ExampleIn most cases you need to use whenSuccess(), whenError() and whenComplete() rather than subscribing via on() because if the instance’s status changes between its construction and your subscription – your handler will not be called:
var async = new MyAsync({...})
// If async is already complete before we call on() - our handler will
// not be triggered:
async.on('success', function (async) { ... })
// So you want to do this:
async.whenSuccess(function (async) { ... })

The call order of handlers is deterministic (Async’s nest() acts as on() because of _childEvents/_forward()) but it’s usually more convenient to use specific priority levels to not depend on the order of nest()/on() calls: on('success') is guaranteed to be called after success-3 but before success2.

Defined in: sqimitive-async.js, line 552

exception ( e )

Called when an exception was thrown during success, error or complete.

Example
async.on('exception', e => console.error(e.message))

In contrast with JavaScript’s Promise an error in handling the “promise” (Async) doesn’t mark that promise failed if its underlying action (e.g. an AJAX call) has succeeded.

If an exception is thrown during an error or success handlers then remaining handlers are skipped, complete is triggered and the exception is re-thrown. An exception during complete also skips remaining handlers of complete and is re-thrown unless there was an exception during error or success.

Other notes:

  • exception’s event listeners are not affected by clear().
  • Async replaces the exception() handler of its children so that the exception is thrown on the top-level Async (whose _parent is not another Async).
  • The above also means that it’s enough to override just the top-level exception() to affect the entire tree.
  • The usual events have “whenXXX()” (whenComplete(), etc.) but there’s no “whenException()” because no info about already occurred errors is stored (like status is stored in _opt). Only future exceptions can be listened to, with the usual on().
  • Unlike success/error/complete, exception doesn’t receive this as an argument.
  • exception isn’t called on request failure unless you throw an exception from error (because Async doesn’t know about your operation’s details).
  • The default implementation throws the argument.

Defined in: sqimitive-async.js, lines 603-605 (3 lines) • Show code

exception: function (e) {
  throw e
},
isLoading ( )
Result Types
Types Notes
true when still loading (status _opt is null)
false when failed or succeeded (isSuccessful)

Defined in: sqimitive-async.js, lines 502-504 (3 lines) • Show code

isLoading: function () {
  return this.get('status') == null
},
isSuccessful ( )
Result Types
Types Notes
null when still isLoading()
true if status _opt is true or ignoreError _opt is set
false

ignoreError affects all methods using isSuccessful(), in particular whenSuccess() and whenError():

async.set('status', false)   // fail it
async.isSuccessful()         //=> false
async.set('ignoreError', true)
async.isSuccessful()         //=> true
async.whenSuccess(() => alert('fired!'))
  // alerts

However, changing ignoreError on run-time is not recommended – see _opt.

Defined in: sqimitive-async.js, lines 495-498 (4 lines) • Show code

isSuccessful: function () {
  var s = this.get('status')
  return s == null ? null : !!(this.get('ignoreError') || s)
},
success ( this )

Called when this instance and children have succeeded.

secsuccess, error and complete are called when this and nested operations have finished (resources loaded, etc.). Consider this analogy with exception handling:

try {
  ...                    // Async.success()
} catch (exception) {
  ...                    // Async.error()
} finally {
  ...                    // Async.complete()
}

But note that “real” exceptions thrown by listeners are handled by exception() outside of the normal pipeline (error’s are expected conditions, exceptions are not).

Each handler is called at most once as if it was bound with once(). If a handler changes the status _opt, others are skipped and events are re-fired.

ExampleIn most cases you need to use whenSuccess(), whenError() and whenComplete() rather than subscribing via on() because if the instance’s status changes between its construction and your subscription – your handler will not be called:
var async = new MyAsync({...})
// If async is already complete before we call on() - our handler will
// not be triggered:
async.on('success', function (async) { ... })
// So you want to do this:
async.whenSuccess(function (async) { ... })

The call order of handlers is deterministic (Async’s nest() acts as on() because of _childEvents/_forward()) but it’s usually more convenient to use specific priority levels to not depend on the order of nest()/on() calls: on('success') is guaranteed to be called after success-3 but before success2.

Defined in: sqimitive-async.js, line 506

toString ( )

Returns a debugger-friendly summary of this object:

The.Class.Name [length] ~¹ STATUS²

¹ ~ is output if the ignoreError _opt is set.

² STATUS is either “LOADING”, “DONE” or “ERROR”

For example:

AsyncImage [2]  DONE

Defined in: sqimitive-async.js, line 268

whenComplete ( func, cx, priority )

Fire func whenever this instance stops being isLoading() – see complete.

Example
$('#spinner').show()
var async = new MyAsync({...})
async.whenComplete(function (async) {
  $('#spinner').hide()
})

// As if:
$('#spinner').show()
try {
  do_async()
} finally {
  $('#spinner').hide()
}

completeDescUnlike with a simple on('event') (on()) func gets called immediately if the condition is already met (then priority is ignored). Otherwise, func is executed before all handlers registered with a larger priority, among (in any order) those with the same and after those with a lower. The value is clamped to the MAX_PRIORITY range. In any case, func is given this (the Async instance).

Warning: the following is incorrect use because func may be called immediately, before the result is assigned to req:

var req = (new Sqimitive.Async).whenComplete(function () {
  // WRONG: req may be undefined:
  req.foo()
})

// CORRECT: assign to the variable first:
var req = new Sqimitive.Async
req.whenComplete(function () { ... })

// CORRECT: or use the passed "this":
;(new Sqimitive.Async)
  .whenComplete(function (req) { ... })

Defined in: sqimitive-async.js, lines 403-405 (3 lines) • Show code

whenComplete: function (func, cx, priority) {
  return this._when(func, cx, priority, !this.isLoading(), 'complete')
},
whenError ( func, cx, priority )

Fire func whenever this instance’s operation has failed – see error.

Example
async.whenError(function (async) {
  alert("Met an error :'(")
})

from completeDesc

Unlike with a simple on('event') (on()) func gets called immediately if the condition is already met (then priority is ignored). Otherwise, func is executed before all handlers registered with a larger priority, among (in any order) those with the same and after those with a lower. The value is clamped to the MAX_PRIORITY range. In any case, func is given this (the Async instance).

Warning: the following is incorrect use because func may be called immediately, before the result is assigned to req:

var req = (new Sqimitive.Async).whenComplete(function () {
  // WRONG: req may be undefined:
  req.foo()
})

// CORRECT: assign to the variable first:
var req = new Sqimitive.Async
req.whenComplete(function () { ... })

// CORRECT: or use the passed "this":
;(new Sqimitive.Async)
  .whenComplete(function (req) { ... })

Defined in: sqimitive-async.js, lines 416-418 (3 lines) • Show code

whenError: function (func, cx, priority) {
  return this._when(func, cx, priority, this.isSuccessful() == false, 'error')
},
whenSuccess ( func, cx, priority )

Fire func whenever this instance’s operation has succeeded – see success.

Example
async.whenSuccess(app.start)

from completeDesc

Unlike with a simple on('event') (on()) func gets called immediately if the condition is already met (then priority is ignored). Otherwise, func is executed before all handlers registered with a larger priority, among (in any order) those with the same and after those with a lower. The value is clamped to the MAX_PRIORITY range. In any case, func is given this (the Async instance).

Warning: the following is incorrect use because func may be called immediately, before the result is assigned to req:

var req = (new Sqimitive.Async).whenComplete(function () {
  // WRONG: req may be undefined:
  req.foo()
})

// CORRECT: assign to the variable first:
var req = new Sqimitive.Async
req.whenComplete(function () { ... })

// CORRECT: or use the passed "this":
;(new Sqimitive.Async)
  .whenComplete(function (req) { ... })

Defined in: sqimitive-async.js, lines 428-430 (3 lines) • Show code

whenSuccess: function (func, cx, priority) {
  return this._when(func, cx, priority, this.isSuccessful() == true, 'success')
},