Posts in this series: Part 1 / Part 2 / Part 3 / Part 4
I just talked to my Thinkful mentor about my first attempt at creating a web app using Steem.js, and he had a lot of tips that I’d like to share for anyone following along. The main points were about promises and how they work.
Promises Are Confusing
In my first post, I mentioned that I ended up using a promise to make sure the API call was complete before utilizing the data. He explained to me that there’s a better way that doesn’t require a promise. Here’s my original code:
let jeffHistoryPromise = new Promise((resolve, reject) => {
steem.api.getAccountHistory('jeffbernst', -1, 1000, function(err, result) {
if (err) reject(err);
resolve(result);
});
});
let totalPowerUpsVal;
jeffHistoryPromise
.then(data => {
totalPowerUpsVal = totalPowerUps(data);
})
.catch(err => {
console.error(err);
});
I ended up using a promise in this way because my first attempt with the API call failed and I thought a promise would solve my problem. But what I didn’t understand was that I can’t return something out of the API call directly. I was trying something like this at first (WRONG):
let totalPowerUpsVal = getHistoryFromApi('jeffbernst');
function getHistoryFromApi(account) {
steem.api.getAccountHistory(account, -1, 1000, function(err, result) {
return totalPowerUps(result);
});
}
This doesn’t work because returning something from the callback function isn’t necessarily going to return it from the whole API call with getAccountHistory. If I understand correctly, the API call will return a promise, not the value I’m returning from the callback function.
The callback function passed into the API call (function(err, result) {}
) gets executed after the result is returned, so I can just utilize the callback as a check on the API call being done. So here’s the updated code (much, much shorter 🙂):
let totalPowerUpsVal;
steem.api.getAccountHistory('jeffbernst', -1, 1000, function(err, result) {
if (!err) totalPowerUpsVal = totalPowerUps(result);
});
Learning More About Promises, Plus Async & Await
After he showed me this cleaner way of taking care of the API call, I asked my mentor if doing multiple API calls at the same time will require promises, and in this case he said it would be a good way to do it.
For my final app, I want to make an API call for 100 different accounts at the same time, then wait for the results and tally up the data. For this I first created a function that wraps the API call in a promise:
function createHistoryPromise(accountName, limit) {
return new Promise((resolve, reject) => {
steem.api.getAccountHistory(accountName, -1, limit, function(err, result) {
if (!err) resolve(totalPowerUps(result));
});
});
}
Then I made an array of some of the accounts I follow on Steemit and mapped the promises onto a new array like this:
let nameArray = [
'jeffbernst',
'exyle',
'louisthomas',
'cryptoctopus',
'dan',
'jerrybanfield',
'ned'
];
let namePromiseArray = nameArray.map(name => createHistoryPromise(name, 1000));
After that, I took my array of promises and used an async
function for the first time. This lets me use the await
keyword to wait until the API call is done before doing anything with the data that it returns. Here’s my function:
let resultsArray = [];
async function resolvePromises() {
for (let i = 0; i < namePromiseArray.length; i++) {
let result = await namePromiseArray[i];
resultsArray.push(`${nameArray[i]}: ${result}`);
}
}
I love how these keywords help make the code easier to understand (well it’s definitely not easy to understand for me at this point, but it definitely identifies something I’m trying to accomplish in the code 🙂). I’m creating a function that will behave asynchronously, and then in let result = await namePromiseArray[i];
you see how I wait for the promise to resolve before assigning it to result
. After that I just have to call my resolvePromises
function and I have a new array with the transactions tallied up.
As I’m still just sending the data directly into the DOM with express, I needed a way to signal in my app.get
request that the data has all been returned and is ready to be displayed. For that I used the following:
let promiseExecution = resolvePromises();
app.get('/', async (req, res) => {
await promiseExecution;
res.send(resultsArray);
});
Using an async function as my callback function means I can once again use the await
keyword to wait for promiseExecution
before sending it with res.send
.
When I start up my server with node app.js
and take a look at localhost:8080
, I see this:
Confirmation that this next iteration is working! It’s so satisfying when the code actually works. 🙂
Up Next
From here I need to figure out a way to target a specific seven day period of time within in the transactions, and implement some logic to catch any potential bugs, such as the transaction history I pull not looking far enough back into the past. At this point I actually feel like I can successfully finish this tool, so I’m excited to keep working on it.
Thanks so much for reading again and please let me know in the comments if you have any questions or corrections! Here is the github repo in case you'd like to take a look.
Posts in this series: Part 1 / Part 2 / Part 3 / Part 4