0

I'm writing an API that gets past transactions on the Stellar network for a user, looks up the corresponding users in my database, and put's it all together into a JSON (to respond to the request - later).

Problem: looking up the username corresponding to the accountID ("from" field) is an async method with mongoose and only returns data after the JSON has been assembled.

I've tried to use async/await, promises, .thens but nothing seems to work.

server.payments()
    .forAccount(accountId)
    .cursor('now')
    .order('desc')
    .limit(2)
    .call()
    .then(function (page) {
        var history = []
        for(var i = 0; i<page.records.length; i++){
            var obj = page.records[i]
            //Get the username corresponding to the key from my database
            //FIXME
            var username
            findUser(obj["from"]).then(result => {
                username = result
            })
            var payment = {
                "currency":obj["asset_code"],
                "from": obj["from"],
                "username":username,
                "amount": obj["amount"],
                "timestamp":obj["created_at"]
            }
            history.push(payment)
        }
        console.log(history)
        //console.log(JSON.stringify(history))
    })
    .catch((err) => {
        console.log(err)
    })

async function findUser(senderKey){
    var user = await mongoose.User.findOne({publicKey: senderKey})
    console.log(user.username)
    return user.username
}

Expected result: findUser returns value, payment variable uses it and gets logged together.

What happens: findUser starts looking for data, payment variable gets put together (username as undefined) and logged, findUser returns data.

Here's the log: (spiderman is the actual username from my database)

[ { currency: 'MEUR',
    from: 'GACRQARPR2OMWRG6IH7HM5DYTA3FMM6UKA7NKS4BIJIADRIKFRPAIE7G',
    username: undefined,
    amount: '3.0000000',
    timestamp: '2019-05-07T13:37:04Z' },
  { currency: 'MEUR',
    from: 'GACRQARPR2OMWRG6IH7HM5DYTA3FMM6UKA7NKS4BIJIADRIKFRPAIE7G',
    username: undefined,
    amount: '2.0000000',
    timestamp: '2019-05-07T13:34:21Z' } ]
spiderman
spiderman

3 Answers3

2

Highlight recommend, you can make it easy with async/await syntax for your case.

You will wait until the server responses the page, then each of obj in page.records (.map or .forEach... will make you some mess), you wait for findUser function returns the username, then do your business.

try {
  const page = await server.payments()
    .forAccount(accountId)
    .cursor('now')
    .order('desc')
    .limit(2)
    .call()
  const history = [];
  for (const obj of page.records) {
    const username = await findUser(obj["from"]);
    const payment = {
      "currency": obj["asset_code"],
      "from": obj["from"],
      "username": username,
      "amount": obj["amount"],
      "timestamp": obj["created_at"]
    }
    history.push(payment)
  }
  console.log(history)
} catch (e) {
  console.log(e)
}

Remind: await expression only allowed within an async function.

hoangdv
  • 15,138
  • 4
  • 27
  • 48
1

You can use the new "for of" loop along with async await :-

server.payments()
    .forAccount(accountId)
    .cursor('now')
    .order('desc')
    .limit(2)
    .call()
    .then(async function (page) {
        var history = []

        for(const obj of page.records) {
          const username = await findUser(obj["from"]);
            var payment = {
                "currency":obj["asset_code"],
                "from": obj["from"],
                "username":username,
                "amount": obj["amount"],
                "timestamp":obj["created_at"]
            }
            history.push(payment)
        }

        console.log(history)
        //console.log(JSON.stringify(history))
    })
    .catch((err) => {
        console.log(err)
    })
cEeNiKc
  • 1,308
  • 7
  • 13
  • 1
    Alternatively, `bluebird` is great for async/await loops. – Michael Ossig May 08 '19 at 03:10
  • 1
    Using something native is always better than using an outside library which will be slower than the native code. – cEeNiKc May 08 '19 at 03:13
  • 1
    I missed the async declaration in the .then() function before, that's why it didn't work when I tried with `await finduser()`. It works, thank you. –  May 08 '19 at 08:26
  • 1
    huh, thats funny @cEeNiKc, last i remember, bluebird is actually faster and lighter for promise handling than V8, you know because its _optimized for promises_ – Michael Ossig May 09 '19 at 04:53
  • 1
    oh wow, look at that, 4x faster! https://softwareengineering.stackexchange.com/questions/278778/why-are-native-es6-promises-slower-and-more-memory-intensive-than-bluebird and if you like numbers when compared to ES7: https://softwareengineering.stackexchange.com/questions/331991/are-native-promises-still-slower-in-node-vs-libraries-such-as-bluebird?noredirect=1&lq=1 – Michael Ossig May 09 '19 at 04:56
  • Mate we are not dealing here with creating new promises. We already get a promise from mongoose that we need to handle. Benchmark is using new Promise syntax but we are not dealing with that. Please read the links you have shared properly. – cEeNiKc May 09 '19 at 05:03
  • And thank you for pointing it out, learned something new today. – cEeNiKc May 09 '19 at 05:09
  • @cEeNiKc does he not want to handle a collection where each item awaits the result of a promise? If you read through the links I shared, Promise handling with collections remains something that bluebird is superior at compared to native JS – Michael Ossig May 12 '19 at 03:01
1

You need a conditional that checks whether the function you are awaiting has completed

async function findUser(senderKey){
    var user = await mongoose.User.findOne({publicKey: senderKey})
    if (user){
      console.log(user.username)
      return user.username
    }
}
Michael Ossig
  • 205
  • 2
  • 9
  • 1
    Actually this is not what the problem is Michael. He is simply not waiting for promise to resolve before iterating to the next iteration. – cEeNiKc May 08 '19 at 03:15
  • 1
    Yes, this seems to be the problem to me too. Thing is, I am not sure how to actually wait for the promise. –  May 08 '19 at 08:11
  • 1
    Please check the solution I have provided. That will clear some things. – cEeNiKc May 08 '19 at 08:28