Viewing Steem Blockchain Data in a Web Browser. Part 10: Calculating the Steem Power and Javascript asynchronous vs. synchronous execution

in #steem-js6 years ago

This is Part 10 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 9 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

In this post I will describe how to compute the Steem Power (SP) of a user. The amount of SP depends on the number of vests that a user holds. The number of vests is obtained from the user's account data but it must be converted to units of Steem to make sense to most people. This conversion will require inputs from the Steemit Dynamic Global Properties data which will be used to compute the ratio of steem to vests which can be multiplied by the number of vests to obtain the SP.

Determining the SP requires the use of two methods from the Steemit API. These are the getDynamicGlobalProperties() and getAccounts() methods. Because two sets of data are obtained by an http request, the asynchronicity of retrieving the data must be taken into account and the code must be structured to make sure that data has arrived before it is used in computations.

Mathematical equation for calculating SP

A user’s Steem Power (SP) has units of “steem” and is equal to the user’s “vesting shares” multiplied by the “steem per vests” in the Steemit vesting fund.

SP = vesting_shares*steem_per_vests

“steem_per_vests” is a dynamic property of the Steemit vesting fund and can be calculated from two properties obtained by calling the getDynamicGlobalProperties() function in the Steemit API. These properties are “total_vesting_fund_steem” and “total_vesting_shares”.

steem_per_vests = total_vesting_fund_steem/total_vesting_shares

Computing the "steem_per_vests"

To compute the steem per vests, lets first make a place to store it and a function to calculate it. We will make a global Object named “computedValues” and give it a “steemPerVests” property. Then we will make a function that computes the steem per vests from the output of the getDynamicGlobalProperties() function of the Steemit API and assigns that value to the computedValues.steemPerVests property. We will make a second function called "amount()" that converts the values from getDynamicGlobalProperties() to numbers because their values are strings with two words like "196736168.322 STEEM" and "397529386958.204121 VESTS". Here is the code:

var computedValues = {steemPerVests: 0.005};

function amount(labeledValue){
        return parseFloat(labeledValue.split(" ")[0]);
}

function computeSPV(dgp){
        var tvfs = amount(dgp['total_vesting_fund_steem']);
        var tvs = amount(dgp['total_vesting_shares']);
        computedValues.steemPerVests = tvfs/tvs;
}

Put the above code between the script tags but above the showUserData() function in the HTML document from the previous post using a texteditor. The value of 0.005 for the steemPerVests property of the computedValues Object is just an initialization and will not stay the same.

Before computing steem per vests using this new function, we need to obtain the "Dynamic Global Properties" data Object. To do this, we will call getDynamicGlobalProperties() in the showUserData() function from the previous post in this series like so:

function showUserData() {
        getElementReferences(['nameForm','dataView']);
        getUserInput(htmlElements.nameForm.elements);
        steem.api.getDynamicGlobalProperties(function(err,result){
            computeSPV(result);
            console.log('Steem Per Vests: ',computedValues.steemPerVests);
        });
        steem.api.getAccounts([userInputs.sUser], function(err,result){
          var userData = result[0];
          var dataToTable = {
            User_Name:userData.name,
            STEEM_Balance: userData.balance,
            SBD_Balance: userData.sbd_balance,
            Voting_Power: 0,
            Reputation_Score: 0,
            Reputation_Score_Formatter: 0
          };
          dataToTable.Voting_Power = computeVotingPower(userData);
          dataToTable.Reputation_Score_Formatter = steem.formatter.reputation(userData.reputation);
          dataToTable.Reputation_Score = computeReputationScore(userData);
          var theText = generateTable('Metric', 'Value',dataToTable);
          htmlElements.dataView.innerHTML = theText;
        });
 }

"steem.api.getDynamicGlobalProperties()" is called on the third line of the "showUserData()" function. It takes no inputs besides a callback function. In the callback function, we send the result fetched by the "getDynamicGlobalProperties()" function to our "computeSPV()" function and "computedValues.steemPerVests" is calculated. We then output "computedValues.steemPerVests" to the console. If you use a text editor to put this new code into the HTML document from the previous post along with the functions and Object declaration to compute "steem_per_vests" shown above, load the new document into a web browser, open the Developer Console, enter a steemit user's name in the text field, and press the submit button, the web browser should look something like this:
Screen Shot 2018-09-26 at 2.27.01 PM.png

When I ran the code on 9/26/2018, the value of computedValues.steemPerVests was:

0.0004948972807112119 STEEM/VESTS.

Convert a user's vesting shares into SP and display it

Now that we know the value of "steem_per_vests", we can convert a user’s vesting shares into SP and display it in the table in the web browser. To do this, we can call "steem.api.getAccounts()" after we call the "steem.api.getDynamicGlobalProperties()" and then compute the SP in the callback function that is passed to "getAccounts()" and put the SP in the table. We will add a property called "STEEM_POWER" to the "dataToTable" object and compute it's value using:

dataToTable.STEEM_POWER = amount(userData.vesting_shares)*computedValues.steemPerVests;

The new call to getAccounts() looks like this:

steem.api.getAccounts([userInputs.sUser], function(err,result){
          var userData = result[0];
          var dataToTable = {
            User_Name: userData.name,
            STEEM_Balance: userData.balance,
            SBD_Balance: userData.sbd_balance,
            Voting_Power: 0,
            Reputation_Score: 0,
            Reputation_Score_Formatter: 0,
            STEEM_POWER: 0
          };
          dataToTable.Voting_Power = computeVotingPower(userData);
          dataToTable.Reputation_Score_Formatter = steem.formatter.reputation(userData.reputation);
          dataToTable.Reputation_Score = computeReputationScore(userData);
          dataToTable.STEEM_POWER = amount(userData.vesting_shares)*computedValues.steemPerVests;
          var theText = generateTable('Metric', 'Value',dataToTable);
          htmlElements.dataView.innerHTML = theText;
});

If you substitute this new "getAccounts()" function call into the "showUserData()" function after the call to "getDynamicGlobalProperties()" and load the HTML document into a web browser, you should see a reasonable number for the SP in the table about 50-75% of the time like this:
Screen Shot 2018-09-26 at 2.51.04 PM.png

If you don't see the right SP number, try clicking the "submit" button again and you should see the right number. The results are unreliable because the "getDynamicGlobalProperties()" and "getAccounts()" functions fetch data from a remote location in an "asynchronous" manner and the results from "getAccounts()" have likely returned before the results from "getDynamicGlobalProperties()". We will fix this below by nesting the function calls.

"Asynchronous" vs. "Synchronous" in Javascript

Usually the word “synchronous” means “happening at the same time” and “asynchronous” means "happening out of phase". Synchronous and asynchronous in Javascript have a different type of meaning. The words refer to synchronous and asynchronous communications. An example of a synchronous communication is when you make a telephone call and someone answers the phone and you ask a question and get a response from them and then you both hang up. An example of an asynchronous communication is if you make a telephone call and ask a question on an answering machine and then the person calls you and gives you a response at the earliest possible convenience. There is a time lag between when you make the call and when you get a response in the asynchronous communication.

Data requests in http protocol are handled much the same way as the answering machine example. Your code calls up the data base and its request is put on a queue and the request is fulfilled as soon as the database can respond. Of course, this takes some time and Javascript does not wait for idle processes to be fulfilled. It moves on to the next block of code and gets fulfillment from previous blocks when they are ready. Therefore, it is possible that you could start trying to use the dynamicGlobalProperties or userAccount data in your code before they are returned from the database.

How to test that the functions are asynchronous

To illustrate asynchronous behavior to yourself, modify the showUserData() function like so:

function showUserData() {
        console.log('1. Getting Element References');
        getElementReferences(['nameForm','dataView']);
        console.log('2. Getting User input');
        getUserInput(htmlElements.nameForm.elements);
        console.log('3. getting dyanamic global properties');
        steem.api.getDynamicGlobalProperties(function(err,result){
          computeSPV(result);
          console.log('4. In gdp',result);
        });
        console.log('5. After gdp');
        console.log('6. getting Accounts');
        steem.api.getAccounts([userInputs.sUser], function(err,result){
          console.log('7. In getAccounts',result);
          var userData = result[0];
          var dataToTable = {
            User_Name: userData.name,
            STEEM_Balance: userData.balance,
            SBD_Balance: userData.sbd_balance,
            Voting_Power: 0,
            Reputation_Score: 0,
            Reputation_Score_Formatter: 0,
            STEEM_POWER: 0
          };
          dataToTable.Voting_Power = computeVotingPower(userData);
          dataToTable.Reputation_Score_Formatter = steem.formatter.reputation(userData.reputation);
          dataToTable.Reputation_Score = computeReputationScore(userData);
          dataToTable.STEEM_POWER = amount(userData.vesting_shares)*computedValues.steemPerVests;
          var theText = generateTable('Metric', 'Value',dataToTable);
          htmlElements.dataView.innerHTML = theText;
        });
        console.log('8. After getAccounts');
}

If you use your texteditor to put in the console.log() instructions, load the HTML document into your browser, open the developer console, enter a steemit user's name in the text field, and click the submit button, you should see something like this:
Screen Shot 2018-09-28 at 7.55.19 PM.png
If you examine the console log, you will find that the statement that logs the execution "console.log('8. After getAccounts');" is executed before "getAccounts()" has returned its result (7) and the statement that logs the execution of code after starting "getDynamicGlobalProperties()" (5) is executed before "getDynamicGlobalProperties()" retrieves its result (4). Javascript does not wait for the data from the blockchain to be returned before it executes the next lines of code. The code must be restructured to make sure that things happen in order.

Restructure the API calls by nesting

The simplest way to account for asynchronicity using the API methods we have already seen is to nest the methods. These methods are asynchronous but we can make sure that one has returned its data before the next one fires by nesting the function calls. Instead of calling "getDynamicGlobalProperties()" and "getAccounts()" in series, we nest them like so:

function showUserData() {
        getElementReferences(['nameForm','dataView']);
        getUserInput(htmlElements.nameForm.elements);
        steem.api.getDynamicGlobalProperties(function(err,result){
            computeSPV(result);
            console.log('Steem Per Vests: ',computedValues.steemPerVests);
            steem.api.getAccounts([userInputs.sUser], function(err,result){
              var userData = result[0];
              var dataToTable = {
                User_Name: userData.name,
                STEEM_Balance: userData.balance,
                SBD_Balance: userData.sbd_balance,
                Voting_Power: 0,
                Reputation_Score: 0,
                Reputation_Score_Formatter: 0,
                STEEM_POWER: 0
              };
              dataToTable.Voting_Power = computeVotingPower(userData);
              dataToTable.Reputation_Score_Formatter = steem.formatter.reputation(userData.reputation);
              dataToTable.Reputation_Score = computeReputationScore(userData);
              dataToTable.STEEM_POWER = amount(userData.vesting_shares)*computedValues.steemPerVests;
              var theText = generateTable('Metric', 'Value',dataToTable);
              htmlElements.dataView.innerHTML = theText;
            });
        });
}

This nesting assures us that the "steem.api.userAccounts()" function will be called only after the dynamic global properties data are returned and that the steem power will be computed and the table displayed only after the data Objects from both API calls have returned from the database. The ability to nest is given by the Steemit API via callbacks to deal with the asynchronicity of the http requests.

In the next post, we will learn how to break out the javascript and css into files that are linked to by the HTML document and write a couple of functions to clean up the code and compute delegated, received, and effective SP. In future posts, we will learn other ways to deal with the asynchronous functions in the Steemit Javascript API using Promises and async functions with await statements.

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!

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!

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!

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!

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!

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!

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!

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!
Your post was mentioned in the Steemit Hit Parade in the following category:

  • Comments - Ranked 1 with 255 comments

There were so many comments because there was a problem with a bot. Oops.

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

Award for the number of upvotes received
Your post was the most commented on one day

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 knock out by hardfork

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