Skip to content

Asynchronous API

ahanusa edited this page Jan 28, 2019 · 3 revisions

peasy-js is an asynchronous framework which requires the use of promises or callbacks.

When using peasy-js, you must choose which asynchronous pattern to use. Mixing promises with callbacks is not supported in peasy-js and will not work.

Using promises

Below is an example of creating peasy-js actors using the async promise pattern.

class PersonDataProxy {

  constructor() {
    this._data = [];
  }

  getById(id) {
    var person = this._findBy(id);
    return Promise.resolve(person);
  }

  getAll() {
    return Promise.resolve(this._data);
  }

  insert(data) {
    data.id = this._data.length + 1;
    var newPerson = Object.assign({}, data);
    this._data.push(newPerson);
    return Promise.resolve(data);
  }

  update(data) {
    var person = this._findBy(data.id);
    Object.assign(person, data);
    return Promise.resolve(data);
  }

  destroy(id) {
    var person = this._findBy(id);
    var index = this._data.indexOf(person);
    this._data.splice(index, 1);
    return Promise.resolve();
  }

  _findBy(id) {
    var person = this._data.filter((function(p) {
      return p.id === id;
    }))[0];
    return person;
  }
}

class PersonNameRule extends Rule {

  constructor(name) {
    super();
    this.name = name;
  }

  _onValidate() {
    if (this.name === "Jimi Hendrix") {
      this._invalidate("Name cannot be Jimi Hendrix");
    }
    return Promise.resolve();
  }
}

class PersonService extends BusinessService {

  constructor(dataProxy) {
    super(dataProxy);
  }

  _getRulesForInsertCommand(person, context) {
    return Promise.resolve([
      new PersonNameRule(person.name)
    ]);
  }

  _getRulesForUpdateCommand(person, context) {
    return this.dataProxy.getById(person.id).then(result => {
      return new PersonIdentityCheckRule(result);
    });
  }
}

const personService = new PersonService(new PersonDataProxy());
const insertCommand = personService.insertCommand({ name: 'James Hendrix'});

insertCommand.execute().then(result => {
  // do something with result
});

In the example above, notice that each function returns a promise. This is what allows the asynchronous execution pipeline to work.

Notice how many of our function implementations return promises even though there's no actual asynchronous work being performed. This can be alleviated by using autoPromiseWrap functionality to help give your code a cleaner appearance and reduce some work on your part.

Also notice that the signatures have changed for BusinessService functions since v.1.5.1. You can learn more about that here.

Using continuation callbacks

While promises are the favored asynchronous approach, continuation callbacks are still supported for ES5 and ES6. They are not, however, supported in the peasy-js TypeScript definitions (promises must be used).

Below is an example of creating peasy-js actors using the continuation callback pattern.

Notice that all of the function definitions below are supplied with a completion function (done) as the last argument.

class PersonDataProxy {

  constructor() {
    this._data = [];
  }

  getById(id, done) {
    var person = this._findBy(id);
    done(null, person);
  }

  getAll(done) {
    done(null, this._data);
  }

  insert(data, done) {
    data.id = this._data.length + 1;
    var newPerson = Object.assign({}, data);
    this._data.push(newPerson);
    done(null, data);
  }

  update(data, done) {
    var person = this._findBy(data.id);
    Object.assign(person, data);
    done(null, data);
  }

  destroy(id, done) {
    var person = this._findBy(id);
    var index = this._data.indexOf(person);
    this._data.splice(index, 1);
    done();
  }

  _findBy(id) {
    var person = this._data.filter((function(p) {
      return p.id === id;
    }))[0];
    return person;
  }
}

class PersonNameRule extends Rule {

  constructor(name) {
    super();
    this.name = name;
  }

  _onValidate(done) {
    if (this.name === "Jimi Hendrix") {
      this._invalidate("Name cannot be Jimi Hendrix");
    }
    done();
  }
}

class PersonService extends BusinessService {

  constructor(dataProxy) {
    super(dataProxy);
  }

  _getRulesForInsertCommand(person, context, done) {
    done(null, [
      new PersonNameRule(person.name)
    ]);
  }

  _getRulesForUpdateCommand(person, context, done) {
    this.dataProxy.getById(person.id, (err, result) => {
      done(err, new PersonIdentityCheckRule(result));
    });
  }
}

const personService = new PersonService(new PersonDataProxy());
const insertCommand = personService.insertCommand({ name: 'James Hendrix'});

insertCommand.execute(function(err, result) {
  // do something with result
});

In each function declaration, a callback function is supplied that accepts the err and result parameters.

err represents handled exceptions, whereas result represents the result of the execution pipeline, and may contain errors (not exceptions) that were produced as a result of rule executions.

Also notice that the signatures have changed for BusinessService functions since v.1.5.1. You can learn more about that here.

Clone this wiki locally