Zeeshan Ahmad Khalil
Zeeshan Ahmad Khalil

Reputation: 861

Executing a code after a promise with nested promises inside it is resolved

I have the following js code structure;

Promise_1.then(function(){
  for(){
    Promise2.then(function(){
      ...
    })
  }
}).then(
  Promise_3.then(function(){
    for(){
      Promise4.then(function(){
        ...
      })
    }
  })
).then(
  function(){
    // SOME CODE
  }
)

I want to execute SOME CODE after the above promises are resolved. But the SOME CODE is executing before the above promises are resolved. I know I can enclose SOME CODE in setTimeout() which will solve the problem as suggested by other answers on SO but I think it's not a good idea. The actual code on which I am working is as follow;

user_xp = 0
connections_blocks.methods.get_deposit_blocks(current_email).call({
    from: new_web3.eth.Contract.defaultAccount
}, function (err, result) {
    deposit_blocks = result
    console.log(deposit_blocks)
    deposit_blocks = deposit_blocks.split(",")
    deposit_blocks.splice(0, 1)
    for (i_ in deposit_blocks) {
        console.log(deposit_blocks[i_])
        user_xp + new_web3.eth.getBlock(deposit_blocks[i_]).then(function (deposit_block) {
            user_xp = user_xp + deposit_block.gasUsed
            console.log(user_xp)
        })
    }
}).then(
    connections_blocks.methods.get_send_blocks(current_email).call({
        from: new_web3.eth.Contract.defaultAccount
    }, function (err, result) {
        send_blocks = result
        console.log(send_blocks)
        send_blocks = send_blocks.split(",")
        send_blocks.splice(0, 1)
        for (i_ = 0; i_ < send_blocks.length; i_ = i_ + 2) {
            console.log(send_blocks[i_])
            user_xp + new_web3.eth.getBlock(send_blocks[i_]).then(function (send_block) {
                user_xp = user_xp + send_block.gasUsed
                console.log(user_xp)
            })
        }
    })).then(
    setTimeout(function () {
        console.log(user_xp)
        xp = document.getElementById('xp')
        xp.innerHTML = "XP:" + user_xp
    },1000)
)

In the above code, I am just using setTimeout() which is solving my problem. But I want the code to execute automatically only when the above promises are resolved. Is there any easy way to do that in JS w/o putting promises in functions and making it more complex.

UPDATE

I am using the following web3's function to fetch data from the solidity's smart contract which actually returns a promise with the data as promiseValue

myContract.methods.myMethod([parameters).call(options,[callback])

Upvotes: 0

Views: 67

Answers (2)

Jaromanda X
Jaromanda X

Reputation: 1

Problems with your code include

  1. the arguments to .then must be functions
  2. if you need to wait for the Promises in the loop, you need to add them to an array and then use promise.all to wait until they're all resolved

Try the following changes to your actual code - I'm assuming that without the node style callback, the function returns a promise that resolves to the result that would be passed to the node-style callback (which is removed in this code)

(if you wait a while I'll flatten the promise chain - just noticed it isn't as flat as it could be

user_xp = 0
return connections_blocks.methods.get_deposit_blocks(current_email).call({
    from: new_web3.eth.Contract.defaultAccount
})
.then(function (result) {
    var promises = []
    deposit_blocks = result
    console.log(deposit_blocks)
    deposit_blocks = deposit_blocks.split(",")
    deposit_blocks.splice(0, 1)
    for (i_ in deposit_blocks) {
        console.log(deposit_blocks[i_])
        promises.push(
            new_web3.eth.getBlock(deposit_blocks[i_]).then(function (deposit_block) {
                user_xp = user_xp + deposit_block.gasUsed
                console.log(user_xp)
            })
        )
    }
    return Promise.all(promises)
})
.then(function () {
    return connections_blocks.methods.get_send_blocks(current_email).call({
        from: new_web3.eth.Contract.defaultAccount
    })
})
.then(function (result) {
    var promises=[]
    send_blocks = result
    console.log(send_blocks)
    send_blocks = send_blocks.split(",")
    send_blocks.splice(0, 1)
    for (i_ = 0; i_ < send_blocks.length; i_ = i_ + 2) {
        console.log(send_blocks[i_])
        promises.push(
            new_web3.eth.getBlock(send_blocks[i_]).then(function (send_block) {
                user_xp = user_xp + send_block.gasUsed
                console.log(user_xp)
            })
        )
    }
    return Promise.all(promises)
})
.then(function () {
    console.log(user_xp)
    xp = document.getElementById('xp')
    xp.innerHTML = "XP:" + user_xp
})

As an added "bonus", I believe the code can be simplified to

return connections_blocks.methods.get_deposit_blocks(current_email).call({
    from: new_web3.eth.Contract.defaultAccount
})
.then(result => Promise.all(result.split(",").slice(1).map(deposit_block => new_web3.eth.getBlock(deposit_block)
    .then(deposit_block => deposit_block.gasUsed)
})
.then(gasUsed1Array => connections_blocks.methods.get_send_blocks(current_email).call({
        from: new_web3.eth.Contract.defaultAccount
    })
    .then(result => Promise.all(result.split(",").slice(1).filter((v, i) => !(i%2)).map(send_blocks => new_web3.eth.getBlock(send_block)
        .then(send_block => send_block.gasUsed)
    )))
    .then(gasUsed2Array => [...gasUsed1Array, ...gasUsed2Array])
)
.then(results => {
    user_xp = results.reduce((a, b) => a + b);
    console.log(user_xp)
    xp = document.getElementById('xp')
    xp.innerHTML = "XP:" + user_xp
})

Upvotes: 1

fubar
fubar

Reputation: 383

I presume that the resolve of Promise_1 and Promise_3 will wait for the callbacks to complete. If this is the case, this should work.

user_xp = 0

connections_blocks.methods.get_deposit_blocks(current_email).call({
    from: new_web3.eth.Contract.defaultAccount
}, function (err, result) {
    deposit_blocks = result
    console.log(deposit_blocks)
    deposit_blocks = deposit_blocks.split(",")
    deposit_blocks.splice(0, 1)
    // array to hold the promises
    const subPromises = []
    for (i_ in deposit_blocks) {
        console.log(deposit_blocks[i_])
        subPromises.push(new_web3.eth.getBlock(deposit_blocks[i_]))
    }
    // waiting for all promises to resolve
    Promise.all(subPromises).then(function (deposit_block) {
        user_xp = user_xp + deposit_block.gasUsed
        console.log(user_xp)
    })
})
.then(
    connections_blocks.methods.get_send_blocks(current_email).call({
        from: new_web3.eth.Contract.defaultAccount
    }, function (err, result) {
        send_blocks = result
        console.log(send_blocks)
        send_blocks = send_blocks.split(",")
        send_blocks.splice(0, 1)
        const subPromises = []
        for (i_ = 0; i_ < send_blocks.length; i_ = i_ + 2) {
            console.log(send_blocks[i_])
            subPromises.push(
                new_web3.eth.getBlock(send_blocks[i_])
            )
        }
        Promise.all(subPromises).then(function (send_block) {
            user_xp = user_xp + send_block.gasUsed
            console.log(user_xp)
        })
    })
)
.then(function () {
    console.log(user_xp)
    xp = document.getElementById('xp')
    xp.innerHTML = "XP:" + user_xp
})

Upvotes: 1

Related Questions