Sequential asynchronous code works, but parallel execution encounters issues

My asynchronous code works flawlessly when executed in series using await, however, as soon as I attempt to run it in parallel using .then() for better performance, I encounter null reference errors.

The use of puppeteer in the launch method is not the issue here since that part functions correctly. I have included the code snippet just to show that async is being used where necessary. While I suspect the problem lies in my implementation of async and await, I am unsure whether I should switch to returning promises instead of using await or if I should utilize Promise.all instead?

            //results is null
            for (const token of allTokens) {
                let url = "https://bscscan.com/token/" + token.contractAddress;
                this.provider.BscScanService.getTokenDataFromWebsite(url).then((results: ScrapeResults) => {
                    console.log("Scraperesults: ", results);
                    this.provider.TokenHolderCountRepo.insertNewTokenHolderCount({
                        tokenId: token.id,
                        holderCount: results.holderCount,
                        date: Date.now()
                    });
                    this.provider.TokenIconRepo.upsertNewIconSource({
                        tokenId: token.id,
                        iconSrc: results.tokenIconSrc
                    });
                    console.log("Done inserts");
                });
            }

            //Working
            for (const token of allTokens) {
                let url = "https://bscscan.com/token/" + token.contractAddress;
                let results:ScrapeResults = await this.provider.BscScanService.getTokenDataFromWebsite(url);
                console.log("Scraperesults: ", results);
                await this.provider.TokenHolderCountRepo.insertNewTokenHolderCount({
                    tokenId: token.id,
                    holderCount: results.holderCount,
                    date: Date.now()
                });
                await this.provider.TokenIconRepo.upsertNewIconSource({
                    tokenId: token.id,
                    iconSrc: results.tokenIconSrc
                });
                console.log("Done inserts");
            }

Other methods

public async getTokenDataFromWebsite(url: string): Promise<ScrapeResults>{
        return await this.scrapingService.getTrackerData(url);
    }

public async getTrackerData(url: string): Promise<ScrapeResults> {
        let scrapeResult: ScrapeResults;

        try
        {
            scrapeResult = await this.scraper.launch(url);
        }
        catch(err){
            return null;
        }

        return scrapeResult;
    }

async launch(url: string): Promise<ScrapeResults>{
        console.log("Accessing URL: " + url);
        puppeteer.use(StealthPlugin());
        console.log("1");
        const browser = await puppeteer.launch({
            //@ts-ignore
            headless: true,
            args: ['--no-sandbox','--disable-setuid-sandbox']
        })
        console.log("2");
        const userAgent = new UserAgent();
        console.log("3");
        let page = await browser.newPage();
        page.setDefaultNavigationTimeout(0);
        console.log("4");
        await page.goto(url);
        await page.setUserAgent(userAgent.toString());
        await page.waitForSelector('body');
        let pageTitle = await page.title();
        console.log("5");
        console.log("Start eval");
        let holderCount: string[] = await page.$$eval(
            '#ContentPlaceHolder1_tr_tokenHolders > div.row.align-items-center > div.col-md-8 > div.d-flex.align-items-center > div.mr-3',
            elements => elements.map(element => element.textContent));
        let iconSource: string[] = await page.$$eval('img.u-sm-avatar.mr-2[src]', elements => elements.map(img => img.getAttribute('src')));
        let tokenName: string[] = await page.$$eval('div.media-body > span.text-secondary.small', elements => elements.map(element => element.textContent))
        const images = await page.evaluate(() => Array.from(document.images, e => e.src));
        await browser.close();
        console.log("End eval");

        console.log("Holdercount:", holderCount);
        console.log("iconSource:", iconSource);
        console.log("tokenName:", tokenName);

        return {
            holderCount: this.getHolderCountFromWebsiteString(holderCount[0]),
            tokenIconSrc: iconSource[0],
            tokenName: tokenName[0]
        };
    }

Answer №1

In my opinion, using Promise.all is the way to go in this situation. You'll need to ensure that getTokenDataForWebsite finishes before moving on to both upsertNewIconSource and insertNewTokenHolderCount , which can be easily achieved with Promise.all.

It's important to note that async operations are not truly parallel, so don't rely on them solely for performance gains. Even though tasks may seem to run concurrently, they still happen on a single thread and might occur in a different order than expected.

You might want to consider implementing something like:

 for (const token of allTokens) {
                let url = "https://bscscan.com/token/" + token.contractAddress;
                let results:ScrapeResults = await this.provider.BscScanService.getTokenDataFromWebsite(url);
              
                await Promise.all([this.provider.TokenHolderCountRepo.insertNewTokenHolderCount({
                    tokenId: token.id,
                    holderCount: results.holderCount,
                    date: Date.now()
                }), this.provider.TokenIconRepo.upsertNewIconSource({
                    tokenId: token.id,
                    iconSrc: results.tokenIconSrc
                })]);
               
            }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Numerous status buttons with a variety of background colors

There are 3 buttons on the order page: [New Order], [Processing], and [Completed]. When the processing button is clicked, the background of the button should change and the order status will be updated in the database. The [New Order] button will have a ...

How to build a registration form with Stateless Components?

Could someone provide a sample code or explanation on how to create a form using stateless components? I'm also in need of a Material UI form example that utilizes refs. Please note that I am working with Material UI components. Below is the curren ...

"Exploring the world of Typescript with the Drawflow library

Currently, I am integrating the fantastic Drawflow library created by @Jerosoler (available at: https://github.com/jerosoler/Drawflow) into my PrimeNg project. User @BobBDE has provided typescript definitions for this library here: https://www.npmjs.com/p ...

Angular 2+ enables the creation of dynamic folders and allows for uploading multiple files to the server in a seamless

Can an Angular application be developed similar to FileZilla or Core FTP for uploading files and folders to the server via FTP? I need a way to upload files and folders through the admin panel instead of relying on external applications like FileZilla. ...

Styling individual table cells with CSS based on their contents of containing another table

I need help with styling a html table that contains another html table. Specifically, I want to apply different css rules (padding and margin) to the td element that holds the child table. My goal is to set padding: 5px; for all td elements except the one ...

Text color animation effect for menu

Here is the layout of my menu: <nav> <a href="#"> <span class="black">Home</span> <span class="red active">Home</span> </a> <a href="#"> <span class="black">Longer m ...

Implementing a FadeOut effect for the clicked link

When clicking on each link, I want the same link to fadeOut() after clicking on the ok button in the myalert() function. If clicked on cancel, it should not fadeOut(). How can I achieve this using the myalert() function? For example: http://jsfiddle.net/M ...

Need help with converting a timestamp based on the user's timezone?

I am receiving a timestamp from the server in the format: " 2022-12-21 16:47:10 ". I need to convert this time to the client's local time zone. For example, 16:47:10 in Poland might be 10am in the US. Do you have any suggestions on how to ac ...

VSC does not seem to recognize the term 'Deno' as it is highlighted in red. I am receiving an error message stating 'Cannot find the name 'Deno'.ts(2304)'. However, Deno is enabled in the settings.json configuration file

I'm facing an issue where Visual Studio Code does not seem to recognize CLI "Deno.args" in the code snippet below. I've already confirmed that deno is enabled in my settings.json. import { readStringDelim } from "https://deno.land/std/io/buf ...

No headers were found in the response from Apollo Client's RetryLink

I have added a RetryLink to my Apollo Client in the following way: const retryLink = new RetryLink({ attempts: (count, _, error) => { return count <= 5 && !!error; }, delay: (count, operation) => { const {response: { headers ...

What is the location to enable devtools for vue.js?

Here is the main.js file of my Vue app: import VueResource from 'vue-resource' import VueRouter from 'vue-router' import Routes from './routes' import App from './App.vue' import Vue from 'vue' import &apo ...

Utilize decorators to access function parameters

Recently, I delved into the realm of creating decorators and learning how to implement them effectively. One question that came up was whether it's possible to access the arguments of a method within the decorator function. For instance, I'm int ...

Unraveling the complexities of Typescript's Advanced Type NonFunctionPropertyNames

Delving deeper into the realm of advanced types in Typescript, I came across an intriguing type called NonFunctionPropertyNames. This type is designed to extract only the properties of a given object that are not functions. type NonFunctionPropertyNames&l ...

Incorporating NodeJS to efficiently parse HTML content and locate specific strings repeatedly

I'm using puppeteer to load a website and save the HTML content of that site using: html = await page.evaluate('new XMLSerializer().serializeToString(document.doctype) + document.documentElement.outerHTML'); It's working well, retriev ...

What is the method to retrieve the data type of the initial element within an array?

Within my array, there are different types of items: const x = ['y', 2, true]; I am trying to determine the type of the first element (which is a string in this case because 'y' is a string). I experimented with 3 approaches: I rec ...

Converting PHP queries into JSON format

Currently, I am running a query on my database using the command Select * from Customer. The 'Customer' table has columns for Name, Surname, Address, and Age. I am looking to convert the query results into a JSON object structured like this: Cu ...

Can Selenium in JavaScript be used to retrieve child elements from a webpage?

I've been struggling to adapt my JavaScript script for use with Selenium (also in JavaScript). Despite numerous attempts, I haven't been able to find a solution. I've attached an image to better explain my question await chromeDriver.findEle ...

Activate divs with Bootstrap5 modal toggle functionality

What adjustments must be made to the Bootstrap 5 example below in order to achieve the following two objectives: The "afterAcceptingTerms" division should remain hidden until the user clicks on the Accept Terms modal button, ensuring that only the "before ...

Automatically refreshing the canvas whenever the value of an HTML element is modified in Javascript

Below is the javascript code that is relevant <script> $.fn.ready(function() { var source = $('#source').val(); Meme('url1', 'canvas','',''); $('#top-line, #bottom-li ...

Creating unique layouts for mobile and desktop using vuejs

Searching for a sleek and intelligent method to offer users varying layouts or components based on mobile or desktop views using Vue. My web application includes many tables, but we all know that tables can be clunky on a mobile device. I'm consideri ...