2

I have an array of strings in my Contract.sol:

string[] messages;

function getMessage(uint i) constant returns (string) {
     require(messages.length > i);
     return messages[i];
}

In my app.js, when the page is loaded I want to populate the page with all the strings. The relevant part of how that's done is as follows:

for (i = numberOfMessages - 1; i >= 0; i--) {
        contractInstance.getMessage.call([i]).then(function (stringReturned) {
          p = document.createElement('p')
          p.innerHTML = stringReturned
          document.body.appendChild(p)
        })
      }

(You can see this is done backwards, ie. the last element in the array is retrieved and added first - because I want to display the latest message first)

The problem with this is the order the strings are retrieved and displayed on the page is all over the place, I guess because the calls are happening very quickly all at once. How should I be doing this?

2

Writing an answer rather than a comment given my reputation. Is contractInstance.getMessage.call an asynchronous call? If it is, then that would explain why you get your results in seemingly random order. You should use a Promise to keep your execution synchronous.

| improve this answer | |
  • It looks like the issue is the calls are being made asynchronously, but I'm not sure about the syntax to make a synchronous call with web3 – ZhouW Jul 12 '17 at 10:15
  • Implementing promises in web3 is the same as any other flavor of JS. You could do it the old fashioned way, and force this snippet to execute synchronously using callbacks. This, of course, would slow down the execution because only one call can be made at a time. The best option, because it would also as a result significantly reduce the amount of gas needed to get the strings from the contract would be to serve a single string, to be parsed by web3, rather than the individual elements of the array. For example, instead of serving, {"a", "b", "c}, you could serve "a&b&c", and split it. – matthewninja Jul 12 '17 at 14:49
2

This was solved by using an async function and await, like as follows:

const asyncFunction = async function() {
        for (i = numberOfMessages - 1; i >= 0; i--) {
          const data = await contractInstance.GetMessage.call(i).then(function (strings) {
              p = document.createElement('p')
              p.innerHTML = strings[0] + strings[1]
              document.body.appendChild(p)
          })
        }
      }

asyncFunction().then(() => {
        console.log('done')
      }).catch((e) => {
        console.error(e)
      })
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.