Viewing Steem Blockchain Data in a Web Browser, Part 13: Using Javascript Promises with steem-js "Async" methods

in #steem-js6 years ago (edited)

This is Part 13 of a series of posts describing how to use the Steem Javascript API to view data from the Steem blockchain in a web browser. The previous 12 posts were:

  • Part 1: Text editors and minimal HTML
  • Part 2: Metadata, Unicode, and inline CSS
  • Part 3: Metadata, nav and footer bars, divs, and HTML entities
  • Part 4: Forms, scripts, and the developer console
  • Part 5: Using HTML id attributes to modify HTML elements using Javascript
  • Part 6: Using the Steem API CDN and the getAccounts() function, and examining the account data model
  • Part 7: HTML tables, javascript for/in loop, CSS in the HTML style element
  • Part 8: HTML, CSS, and Javascript comments; Javascript functions and MVC
  • Part 9: Compute Voting Power and Reputation Score, Javascript "const" and "let" with block scope and loop scope
  • Part 10: Calculating the Steem Power and Javascript asynchronous vs. synchronous execution
  • Part 11: Javascript "switch" statement and computing owned, delegated, and received Steem Power
  • Part 12: Splitting the HTML file into HTML, CSS, and js files

In part 10 of this series, I discussed asynchronous vs. synchronous communications and how fetching data from the remote Steemit database is an asynchronous operation. Javascript normally does not wait for data to return from a fetching operation before it passes execution to other lines of code. The steem-js API functions that we have discussed ("getAccounts()" and "getDynamicGlobalProperties()") handle this issue by providing a callback parameter to which the user can pass a function with instructions to execute after the data is returned. This is an older way of structuring asynchronous code with nested callbacks. A newer way is to use the Promise object in which asynchronous operations are wrapped in the Promise object which has a “then()” method which contains a user-supplied function to run after the data is returned.

steem-js methods that return Promises

steem-js provides functions that have a camel-case "Async" affixed to them. So, for example, there is a "getAccountsAsync()" function that does what the regular "getAccounts()" does but it returns a Promise and doesn’t expect to have a callback passed to it.
The getAccounts() method:

steem.api.getAccounts(['ned', 'dan'], function(err, result) {
    console.log(err, result);
});

does the same thing as:

const accountPromise = steem.api.getAccountsAsync(['ned', 'dan']);
accountPromise.then(function(result,err){
  console.log(err,result);
});

or

steem.api.getAccountsAsync(['ned', 'dan']).then(function(result,err){
  console.log(err,result);
});

or

steem.api.getAccountsAsync(['ned', 'dan']).then((result,err) =>{
  console.log(err,result);
});

Notice that the order of "err" and "result" is reversed in the nested callback structure compared to the Promise.then() structure.

"getDynamicGlobalProperties()" has no argument besides a callback so "getDynamicGlobalPropertiesAsync()" is called like this:

const dgp = getDynamicGlobalPropertiesAsync();

and the variable named "dgp" is a Promise object.

steem-js achieves returning the promise by wrapping the functions in a Promise object using bluebird. The methods (functions) available that return promises are listed in the steem-js code. The listing contains underscores in the method names because the steem-js library is really a javascript interface to the steem Appbase API. The names are made camelcase and “Async” is appended in the js API.

Promises

Javascript Promises are special kinds of objects that wrap asynchronous operations so that you can specify what to do after and only after the asynchronous operation finishes. Sometimes the asynchronous operation returns a useful result and sometimes it doesn’t. When the useful result (usually data) can not be returned, the Promise is considered to be “rejected” and the "Promise.catch()" method is used to insert error handling code. We won’t deal with errors yet. We will assume that the Promise is “resolved” and that the data is returned properly. Instead of executing a callback when the data is returned, a function in the Promise's “then()” method can be executed and if the function in the "then()" method also returns a Promise, a Promise "chain" can be created.

The function in the "Promise.then()" method can be passed one or two arguments. The first argument is always the result of a "resolved" Promise and the second argument can be an error if the Promise was rejected.

For example:

steem.api.getDynamicGlobalPropertiesAsync().then(function(resolve,reject){
  console.log(resolve,reject);
});

or

steem.api.getDynamicGlobalPropertiesAsync().then(function(resolve){
  console.log(resolve);
});

The arguments to the function in the "then()" method do not have to be “resolve” or “reject”. Any name could be used for the arguments but the first argument will contain a reference to the resolved value of the Promise while the second argument (which is optional) will contain a reference to the rejected value of the Promise if it is rejected. You could just as well write this:

steem.api.getDynamicGlobalPropertiesAsync().then(function(goodResult, badError){
  console.log(goodResult,badError}
); 

and get the same output to the console.

Promise chain

Now I will show how to place our previous nested API calls in a Promise chain. The nested API calls are:

steem.api.getDynamicGlobalProperties(function(err,result){
          computeSPV(result);
          steem.api.getAccounts([userInputs.sUser], function(err,result){
            var userData = result[0];
            var dataToTable = generateTableData(userData,computedValues.steemPerVests);
            var theText = generateTable('Metric', 'Value',dataToTable);
            htmlElements.dataView.innerHTML = theText;
          });
});

The corresponding Promise chain is:

steem.api.getDynamicGlobalPropertiesAsync()
          .then((globalProps)=>{
            computeSPV(globalProps);
            return steem.api.getAccountsAsync([userInputs.sUser]);})
          .then((result) =>{
            var userData = result[0];
            var dataToTable = generateTableData(userData,computedValues.steemPerVests);
            var theText = generateTable('Metric', 'Value',dataToTable);
            htmlElements.dataView.innerHTML = theText;
});

The function in the first "then()" method of the Promise returned by "getDynamicGlobalPropertiesAsync()" returns a Promise because it returns "getAccountsAsync()" which is a Promise. Because that first "then()" method returns a Promise, the second "then()" method will wait for that Promise to resolve or reject before it executes its code.

Promise.all()

The Promise object also has a method called “Promise.all()”. An array of Promises can be passed to the "all()" method and an array of results will be returned when the Promises have resolved or rejected. Using "Promise.all()", the Promise chain can be restructured to return both the data from "getDynamicGlobalProperties()" and "getUserAccounts()" in an array and the "then()" method can be called to do operations on those two data objects. Like so:

Promise.all([steem.api.getDynamicGlobalPropertiesAsync(),
  steem.api.getAccountsAsync([userInputs.sUser])])
            .then((outputs)=>{
              computeSPV(outputs[0]);
              var userData = outputs[1][0];
              var dataToTable = generateTableData(userData, computedValues.steemPerVests);
              var theText = generateTable('Metric', 'Value',dataToTable);
              htmlElements.dataView.innerHTML = theText;
});

The result of the first Promise is put in the first element of the resolved array and the result of the second Promise is put in the second element of the resolved array. The result of the second Promise is an array of users' account data so the first element of that array must be selected using '[0]' to retrieve the user account data. If the account data of more than one user was retrieved using "getAccountsAsync()", 'outputs[1][1]' would be used to access the user account data of the second user. The account data of a third user would be at 'outputs[1][2]' and so on.

The output from using the "Promise.then()" or the "Promise.all()" methods is the same as nesting the callback functions:

Screen Shot 2018-10-11 at 3.42.25 PM.png

Sort:  

Hello! Your post has been resteemed and upvoted by @ilovecoding because we love coding! Keep up good work! Consider upvoting this comment to support the @ilovecoding and increase your future rewards! ^_^ Steem On!

Reply !stop to disable the comment. Thanks!

Congratulations @numberjocky! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 1 year!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Do not miss the last post from @steemitboard:

Are you a DrugWars early adopter? Benvenuto in famiglia!
Vote for @Steemitboard as a witness to get one more award and increased upvotes!

Congratulations @numberjocky! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

Award for the total payout received

Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @steemitboard:

SteemitBoard Ranking update - Resteem and Resteemed added

Support SteemitBoard's project! Vote for its witness and get one more award!

Congratulations @numberjocky! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

You made more than 1250 upvotes. Your next target is to reach 1500 upvotes.

Click here to view your Board of Honor
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @steemitboard:

Be ready for the next contest!
Trick or Treat - Publish your scariest halloween story and win a new badge
SteemitBoard notifications improved

Support SteemitBoard's project! Vote for its witness and get one more award!

Congratulations @numberjocky! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

You made more than 1500 upvotes. Your next target is to reach 1750 upvotes.

Click here to view your Board of Honor
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @steemitboard:

The new Steemfest³ Award is ready!

Support SteemitBoard's project! Vote for its witness and get one more award!

Congratulations @numberjocky! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

You made more than 1750 upvotes. Your next target is to reach 2000 upvotes.

Click here to view your Board of Honor
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @steemitboard:

SteemFest³ - SteemitBoard Contest Teaser
The new Steemfest³ Award is ready!

Support SteemitBoard's project! Vote for its witness and get one more award!

You got a 3.76% upvote from @postpromoter courtesy of @numberjocky!

Want to promote your posts too? Check out the Steem Bot Tracker website for more info. If you would like to support the development of @postpromoter and the bot tracker please vote for @yabapmatt for witness!

Congratulations @numberjocky! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

You made more than 3000 upvotes. Your next target is to reach 4000 upvotes.

Click here to view your Board of Honor
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @steemitboard:

Saint Nicholas challenge for good boys and girls

Support SteemitBoard's project! Vote for its witness and get one more award!

Congratulations @numberjocky! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

You made more than 4000 upvotes. Your next target is to reach 5000 upvotes.

Click here to view your Board
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @steemitboard:

Christmas Challenge - Send a gift to to your friends

Support SteemitBoard's project! Vote for its witness and get one more award!

Congratulations @numberjocky! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

You made more than 5000 upvotes. Your next target is to reach 6000 upvotes.

Click here to view your Board
If you no longer want to receive notifications, reply to this comment with the word STOP

Support SteemitBoard's project! Vote for its witness and get one more award!

Coin Marketplace

STEEM 0.29
TRX 0.12
JST 0.032
BTC 57824.15
ETH 2965.89
USDT 1.00
SBD 3.70