Node.js async await vs promise vs callback

Normally Node.js invokes functions asynchronously. Node.js async await, promise and callback comes into picture when you want to execute one function after completion of another function execution.

All these features are introduced one by one in Node.js different version release. Before Node version 7.6, the callbacks were the only way provided by Node to run one function after another.

We will see all three concepts taking real case scenario:

Three Tomatoes with Shadows --- Image by © Royalty-Free/Corbis

Requirement is If you have sufficient balance then you can purchase. All examples below written can not be directly executed rather used here to show concept difference.

Ex:

let placeAnOrderAndDeliver = function(name, address) {
  // Send the item to Dear 'name' staying at this 'address'
}

let showWarningOrError = function(statusCode, error) {
  // Show warning message or error message if no sufficient balance
  // Or if any error happened
}

let checkSufficientBalanceInAccount = function(
    userName, password, balanceNeeded, callback) {
  if (logInCredentialValidated) {
    if (balanceInMyAccount > balanceNeeded) {
      callback(true);
    } else {
      callback(false);
    }
  } else {
    callback(false);
  }
}

let purchaseItem = function(itemName, itemCost) {
  try {
    checkSufficientBalanceInAccount('testUser', 'testPassword', itemCost,
        function(response) {
          if (response) {
            placeAnOrderAndDeliver('Name', 'Address');
          } else {
            showWarningOrError('402', 'Insufficient balance!')
          }
        })
  } catch (error) {
    showWarningOrError('400', error.message)
  }
}

Problem of above way is, for multiple callbacks it gets messy and cause lot of troubles.

Later promise and function chaining introduced in Node.js version:

So if you will refactor above functions into promise and function chaining, it will look like this:

let placeAnOrderAndDeliver = function(name, address) {
  // Send the item to Dear 'name' staying at this 'address'
}

let showWarningOrError = function(statusCode, error) {
  // Show warning message or error message if no sufficient balance
  // Or if any error happened
}

let checkSufficientBalanceInAccount = new Promise((resolve, reject) => {
  let successMsg = {
    status: '200',
    message: 'Successful transaction!'
  };

  let failureMsg = {
    status: '449',
    message: 'Unsuccessful transaction!'
  };

  let failureLoginMsg = {
    status: '401',
    message: 'Invalid credential!'
  };

  if (logInCredentialValidated) {
    if (balanceInMyAccount > balanceNeeded) {
      resolve(successMsg);
    } else {
      reject(failureLoginMsg);
    }
  } else {
    reject(failureMsg);
  }
});

let purchaseItem = function(itemName, itemCost) {
  try {
    checkSufficientBalanceInAccount('testUser', 'testPassword', itemCost).
        then((response) => {
          placeAnOrderAndDeliver('Name', 'Address');
        }).
        catch((reason) => {
          showWarningOrError(reason.status, reason.message);
        });
  }
  catch (error) {
    showWarningOrError('400', error.message);
  }
};

In this way code looks more clear and easily understandable. You can chain multiple functions by adding multiple .then

In Node v8 await gets released to deal with promise and chaining. Here no need to write .then, Simply you need to call prefix await before the function name and call next function usually you call the function. Unless await function executes the next function will not be executed.

Example using async/await:

let placeAnOrderAndDeliver = function(name, address) {
  // Send the item to Dear 'name' staying at this 'address'
}

let showWarningOrError = function(statusCode, error) {
  // Show warning message or error message if no sufficient balance
  // Or if any error happened
}

let checkSufficientBalanceInAccount = new Promise((resolve, reject) => {
  let successMsg = {
    status: '200',
    message: 'Successful transaction!'
  };

  let failureMsg = {
    status: '449',
    message: 'Unsuccessful transaction!'
  };

  let failureLoginMsg = {
    status: '401',
    message: 'Invalid credential!'
  };

  if (logInCredentialValidated) {
    if (balanceInMyAccount > balanceNeeded) {
      resolve(successMsg);
    } else {
      reject(failureLoginMsg);
    }
  } else {
    reject(failureMsg);
  }
});

let purchaseItem = async function(itemName, itemCost) {
  try {
    await checkSufficientBalanceInAccount('testUser', 'testPassword', itemCost);
    placeAnOrderAndDeliver('Name', 'Address');
  }
  catch (error) {
    showWarningOrError('400', error.message);
  }
};

If you will compare promise based example and async/await based example then you will find last function is the main difference where “let purchaseItem = async function” you can see async keyword before function keyword. Because await should be used inside async function.

Another difference is functions are called in an order, where function called after await function will not execute unless await function completes.

That is all about basic usage and main differences. If you want to know more about promise and async/await go through these references links:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Hope you like it!

Thanks for your time to go through this.

Advertisements

Use of realm object in realm Event handler

Before describing the deep concept of handling realm event handler changes, if you want to know about realm then please go through these links https://realm.io/ and https://realm.io/docs/javascript/latest/once.

Realm Platform:

They provide new standard in data synchronisation. Realm helps companies build engaging mobile applications with minimal development time. The Realm Platform enables any development team, no matter the size, to include difficult-to-build features like two‑way data sync and realtime collaboration. Devote more time to what makes your app truly unique and less time to maintaining services.

Realm object database is a simple alternative to SQLite and Core Data and proudly open source.

I can just share the benefits of realm database quickly:Screen Shot 2018-06-23 at 10.06.14 AM

Now let’s discuss about handling event handler changes:

This is an important and awesome feature which you will not find in any other mobile DB like sqlite, Oracle Lite, Tiny DB etc.

Once you setup realm database and connect to realm DB using Node JS then for any changes in database, the event fires for that change and you can capture that event change in callback.  If you have multiple databases or individual database for each user then you can set the path to register the event change for those databases.

So for each operation(Insertion, Modification and Delete) in database there will be event fired and in callback of that you can do your operation in Node Js. For example if there is data insertion in realm DB through mobile then in Node Js server you can catch the data and insert that data to MySql or move to AWS SQS or play with it.

A sample index.js file might look something like this. This example listens for changes to a user-specific private Realm at the virtual path /~/myuserDB. It will look for updated Coupon objects in these Realms, verify their coupon code if it wasn’t verified yet, and write the result of the verification into the isValid property of the Coupon object.

'use strict';
var Realm = require('realm'); 

// the URL to the Realm Object Server
var SERVER_URL = '//127.0.0.1:9080';

// The regular expression you provide restricts the observed Realm files to only the subset you
// are actually interested in. This is done in a separate step to avoid the cost
// of computing the fine-grained change set if it's not necessary.
var NOTIFIER_PATH = '^/([^/]+)/myUserDB$';

//declare admin user
let adminUser = undefined

// The handleChange callback is called for every observed Realm file whenever it
// has changes. It is called with a change event which contains the path, the Realm,
// a version of the Realm from before the change, and indexes indication all objects
// which were added, deleted, or modified in this change
var handleChange = async function (changeEvent) {
  // Extract the user ID from the virtual path, assuming that we're using
  // a filter which only subscribes us to updates of user-scoped Realms.
  var matches = changeEvent.path.match("^/([^/]+)/([^/]+)$");
  var userId = matches[1];

  var realm = changeEvent.realm;
  var coupons = realm.objects('Coupon');
  var couponIndexes = changeEvent.changes.Coupon.insertions;

  for (let couponIndex of couponIndexes) {
    var coupon = coupons[couponIndex];
    if (coupon.isValid !== undefined) {
      var isValid = verifyCouponForUser(coupon, userId);
      // Attention: Writes here will trigger a subsequent notification.
      // Take care that this doesn't cause infinite changes!
      realm.write(function() {
        coupon.isValid = isValid;
      });
    }
  }
}

function verifyCouponForUser(coupon, userId) {
    //logic for verifying a coupon's validity
}

// register the event handler callback
async function main() {
    adminUser = await Realm.Sync.User.login(`https:${SERVER_URL}`, 'realm-admin', '')
    Realm.Sync.addListener(`realms:${SERVER_URL}`, adminUser, NOTIFIER_PATH, 'change', handleChange);
}

main()

Note: In above code var realm = changeEvent.realm; where realm is the object which contains all rows, values which was changed in DB and other informations as well. So in event handler file no need to open realm to get realm object or to do any DB operation. You can use above realm object through out the event handler file carefully.

In past we did same mistake when dealing with realm object. Like

Realm.open({schema: [Car, Person]})
  .then(realm => {
    // ...use the realm instance here
  })
  .catch(error => {
    // Handle the error here if something went wrong
  });

So you no need to open realm in Event handler file unless you need it. As you can see the object realm already available var realm = changeEvent.realm;  globally.

For any DB operation like insertion, modification and delete operation you can use above realm object.

References:

https://realm.io/docs/javascript/latest/
https://academy.realm.io/posts/realm-sf-tim-oliver-event-handling-realm-object-server/https://docs.realm.io/platform/using-synced-realms/server-side-usage/data-change-events

Hope you like the post and stay tuned for my next post about “deep analysisof handling event handler for each DB operations”.

Thanks!