I can't seem to find where I did or did not use the await keyword right

I have been bugged down for 2 days straight trying to figure out where I messed up but I can’t come to terms
Here’s a full breakdown of the code I wrote

const { library } = useWeb3React();
  const provider = library;
  const router = useRouter();
  const { profile } = router.query;
  const profileData = [];
  useEffect(() => {
    const main = async () => {
      await coll.map(async (col) => {
        const instance = new Contract(col, nftABI, provider);
        const balance = await instance.balanceOf(profile);
        let profileBalance = balance.toNumber();
        const tokenIds = [];
        const name = [];
        const images = [];
        for (let i = 0; i < profileBalance; i++) {
          if (profileBalance > 0) {
            const tokenId = await instance.tokenOfOwnerByIndex(profile, i);
            let NumTokenId = tokenId.toNumber();
            let StrTokenId = String(NumTokenId);
            const uri = await instance.tokenURI(StrTokenId);
            const url = uri.replace(
              "ipfs://",
              "https://gateway.pinata.cloud/ipfs/"
            );
            const ipfsFile = await fetch(url).then((r) => r.json());
            const imageUri = ipfsFile.image.replace(
              "ipfs://",
              "https://gateway.pinata.cloud/ipfs/'
            );
            name.push(ipfsFile.name);
            images.push(imageUri);
            tokenIds.push(StrTokenId);
          }
        }
        const obj = {
          collection: col,
          tokenArray: tokenIds,
          image: images,
          name: name,
        };
        profileData.push(obj);
        console.log(profileData);
      });
    };
    main();
    console.log(profileData);
    setTimeout(() => console.log(profileData), 5000);
  }, []);

The first console.log statement prints the array, one time after another, filling it up with a new object as it iterates through
The second prints an empty array
The third, which I executed in a timeout function, prints the array with all the data fully loaded in
I know for sure that the issue with my code is within the asynchronous nature because the second console.log statement happens before any of the others execute
Guys, what am I missing?

1 Like

Yes, the current order of the logs sounds correct. When you call main(), the async function starts, but the 2nd console.log runs before main is finished. If you want to do something with profileData, you should do it inside the body of main.

If you want to bring that data outside the main block, you need to use state and set the data to it, again, inside the main function body

I updated the code to

  const [r, setR] = useState([]);
  console.log(coll);
  const { library } = useWeb3React();
  const provider = library;
  const router = useRouter();
  const { profile } = router.query;
  useEffect(() => {
    const main = async () => {
      const profileData = [];
      await coll.map(async (col) => {
        const instance = new Contract(col, nftABI, provider);
        const balance = await instance.balanceOf(profile);
        let profileBalance = balance.toNumber();
        const tokenIds = [];
        const name = [];
        const images = [];
        for (let i = 0; i < profileBalance; i++) {
          if (profileBalance > 0) {
            console.log(profileBalance, i, col);
            const tokenId = await instance.tokenOfOwnerByIndex(profile, i);
            let NumTokenId = tokenId.toNumber();
            let StrTokenId = String(NumTokenId);
            const uri = await instance.tokenURI(StrTokenId);
            const url = uri.replace(
              "ipfs://",
              "https://gateway.pinata.cloud/ipfs/"
            );
            const ipfsFile = await fetch(url).then((r) => r.json());
            const imageUri = ipfsFile.image.replace(
              "ipfs://",
              "https://gateway.pinata.cloud/ipfs/"
            );
            name.push(ipfsFile.name);
            images.push(imageUri);
            tokenIds.push(StrTokenId);
          }
        }
        const obj = {
          collection: col,
          tokenArray: tokenIds,
          image: images,
          name: name,
        };
        if (obj.name.length > 0) {
          profileData.push(obj);
          setR(profileData);
        }
      });
      console.log(profileData, "Show data");
    };
    main();
  }, []);
  console.log(r, "Show useState data");

Now, when mapping through the data, it shows all 3 at first, then deprecates to show just the first

{r &&
          r.map((k, i) => (
            <div key={i}>
              Collection: {k.collection}
              <h1>Name: {k.name}</h1>{" "}
              
            </div>
          ))}


This two images, onFirstLoad shows temporarily, then disappears in like a second when the page has completed rendering
loadCompleted paints the screen and becomes the final state
Yet, in the console.log statement for r (useState var I used), all 3 objects are in the variable
I am honestly, very confused

It looks suspicious that the setState is inside the map. The await logic should just be the network call(s), then the setState should happen after that, just once.

I would also clean up the code a bit with try and catch blocks. I would put the fetch logic inside the try block, then the setState after the end of the catch block

1 Like
  useEffect(() => {
    const main = async () => {
      const profileData = [];
      try {
        coll.map(async (col) => {
          const instance = new Contract(col, nftABI, provider);
          const balance = await instance.balanceOf(profile);
          let profileBalance = balance.toNumber();
          const tokenIds = [];
          const name = [];
          const images = [];
          for (let i = 0; i < profileBalance; i++) {
            if (profileBalance > 0) {
              console.log(profileBalance, i, col);
              const tokenId = await instance.tokenOfOwnerByIndex(profile, i);
              let NumTokenId = tokenId.toNumber();
              let StrTokenId = String(NumTokenId);
              const uri = await instance.tokenURI(StrTokenId);
              const url = uri.replace(
                "ipfs://",
                "https://gateway.pinata.cloud/ipfs/"
              );
              const ipfsFile = await fetch(url).then((r) => r.json());
              const imageUri = ipfsFile.image.replace(
                "ipfs://",
                "https://gateway.pinata.cloud/ipfs/"
              );
              name.push(ipfsFile.name);
              images.push(imageUri);
              tokenIds.push(StrTokenId);
            }
          }
          const obj = {
            collection: col,
            tokenArray: tokenIds,
            image: images,
            name: name,
          };
          if (obj.name.length > 0) {
            profileData.push(obj);
          }
        });
      } catch (error) {
        console.log(error);
      }
      setR(profileData);
    };
    main();
  }, []);

I get back an empty array for the useState I get back an empty array for the useState Variable

Not sure why you are using map here since you are not using the returned value of it. Just use a for loop instead. Plus map will not wait for the asynchronous function calls to complete before moving to the next element in the array. Lastly, you should not use higher order functions like map, filter, or reduce to create manipulate variables outside of them. You are iterating through via map to maniuplate profileData.

What does the coll array look like? I am unable to test the code.

It’s an array of NFT contracts that I created for testing an NFT Gallery platform
Would you like to see the full code?
It’s like less than 150 lines long.
But you’ll need dependencies like Web3React
I’ve also tried to use the forEach method and I still have the same issues
The only way the state variable will be populated is if I place it in the .map (or .forEach) method which at the end of the day, doesn’t save the day

The for loop turned out to be the magic.
I guess I must have been using .map for too long, that I’ve failed to recognize the use of the others which are helpful in their ways
Thanks a bunch.
@Hideaki thanks a lot too man for taking your time to examine the code and respond

Glad you figured it out. Yep, if you want a loop that awaits each iteration, a for await is probably what you’re looking for

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.