Skip to content

Sync vs Async Code

The node.js environment does not allow synchronous blocking IO (networking) operations. As a result, any custom code which uses either Backendless API or communicates with external hosts, must use the asynchronous version of APIs. However, there is a special consideration for custom server code which uses asynchronous operations. Specially, any code for which the following two scenarios are true, must return a JavaScript Promise:

  1. performs an asynchronous operation and needs its result to be used as the return value of the event handler/API service;

    or
  2. performs an asynchronous operation which continues to run after the custom code completes its execution. In this case returning a Promise from custom code is an instruction to CodeRunner to wait for the asynchronous operation to finish.

When custom server code returns a Promise, CodeRunner will wait for its completion and will use its resolved/rejected value as the result of event handler or API service.

Execution Flow

The diagram below illustrates the decision flow the CodeRunner goes through when receives a return value from the custom server code (applies to event handlers, timers and API services):

code-runner-promise-analysis

Example

Consider the following example illustrating the approach. Suppose there is a blog management app. There is a task to track the number of comments on a blog post. This can be done by adding an afterCreate event handler to the Comment table, retrieve a related Post object, increment it's comments field and save it. That means the code has to perform two asynchronous operations in a row ("get post" and then "save post"). Also, it means the code must return a Promise from the event handler letting CodeRunner™ know that the event handler has completed its task.

You will find four different implementations with identical logic for this scenario, which should help in understanding how Promises must be used in custom server code in Backendless.

All implementations are configured as asynchronous event handlers (see the "true" argument in the afterCreate API method call - the last last in all examples). This approach provides several benefits in this particular case:

  • asynchronous event handlers run in background without blocking main API invocation chain (comment insertion in our case);
  • the Promise's "resolve" value is not important because it is never sent to the server (for the reason described in the first bullet).

Returning Promise with asynchronous Backendless API wrapped into it:

backendless-async-approach.js

Backendless.ServerCode.Persistence.afterCreate('Comment', function (req) {
    var PostsStore = Backendless.Data.of('Post');

    return new Promise((resolve, reject) => {
        var onError = (err) => {
            reject('Unable to update post comments counter. Got an error : ' + err.message)
        }

        PostsStore.findById(req.item.post, new Backendless.Async(
            post => {
                post.comments++;

                PostsStore.save(post, new Backendless.Async(resolve, onError))
            },
            onError
        ))
    });
}, true)

Enabling promises in Backendless API, then returning result of the Backendless Data Service "save" method - which will be a promise, since the promises are enabled:

backendless-promises-approach.js

Backendless.enablePromises();
Backendless.ServerCode.Persistence.afterCreate('Comment', function (req) {
    var PostsStore = Backendless.Data.of('Post');

    return PostsStore.findById(req.item.post)
        .then(post => {
            post.comments++;

            return PostsStore.save(post);
        })
        .catch(err => {
            return Promise.reject('Unable to update post comments counter. Got an error : ' + err.message)
        });
}, true)

Event handler delegates to persistence item object, which modifies its own property and saves itself:

persistence-item-approach.js

var Post = require('./post');

Backendless.ServerCode.Persistence.afterCreate('Comment', function (req) {
    return Post.findById(req.item.post)
        .then(post => post.incrementCommentsCounter())
        .catch(err => {
            Promise.reject('Unable to update post comments counter. Got an error : ' + err.message);
        });
}, true)

post.js
'use strict'; //required if you want to use es6 classes

class Post extends Backendless.ServerCode.PersistenceItem {
  incrementCommentsCounter() {
    this.comments++;

    return this.save().
  }
};

module.exports = Backendless.ServerCode.addType(Post);