JavaScript: How to convert function to async but return value not in promise?

87 Views Asked by At

I have a unique issue. Let's say I have this code, which runs anonymously in a code block called a "Data Element" (Adobe Launch lingo). The code is assigned the name of the data element's name so it can be referenced in the calling components which could happen 0 to 100s of times on a single page load:

return (function() {

    function main(var1, var2) {
        var thirdPartyResult = thirdpartyLibrary.someMethod(var1, var2);
        var returnVal = false;
        if (thirdPartyResult > 6) {
            returnVal = true;
        }
        return returnVal;
    }
    return main(cat, subCat);// these values are passed in from external source when data element is called.

})();

The above worked fine but I noticed timing issues because when the data element is called early in the page load (after cache clear etc.), the "thirdpartyLibrary" hasn't loaded yet. So for example, the first 3 times it is called during the page loading, the data element throws and error because "thirdpartyLibrary" doesn't exist yet, therefore I can't use the output from it which is required. Regarding the code below which I know obviously doesn't work, is syntactically wrong but expresses and conceptualizes what I would really want to do, I'd only like the main function delayed until the "thirdpartyLibrary" has loaded because my functionality in "main" depends on the "thirdpartyLibrary" but since I had to make the "main" function async, it returns a promise which has nothing to do with the data I want to return and will assign "promise pending" to my return statement ==> return main('20', '4'). I know I can't do this: return await main('20', '4') which would solve my issue or wrap my return in an async function because that wouldn't be accessible. Any idea(s) on what I can possibly do?

return (function() {

    async function waitForLibrary() {
        return new Promise((resolve, reject) => {
            const maxAttempts = 8;
            const interval = 500; // milliseconds
            let attempts = 0;


            function checkLibrary() {
                if (typeof thirdpartyLibrary !== 'undefined' && typeof thirdpartyLibrary.someMethod === 'function') {
                    resolve();
                } else if (attempts < maxAttempts) {
                    attempts++;
                    setTimeout(checkLibrary, interval);
                } else {
                    reject(new Error("Timeout: Third-party library not loaded."));
                }
            }

            checkLibrary();
        });
    }

    async function main(var1, var2) {
        await waitForLibrary();
        var thirdPartyResult = thirdpartyLibrary.someMethod(var1, var2);
        var returnVal = false;
        if (thirdPartyResult > 6) {
            returnVal = true;
        }
        return returnVal;
    }
    return main(cat, subCat);// these values are passed in from external source when data element is called.

})();

Also I'm new to promises. Thanks!

1

There are 1 best solutions below

0
On

There are many things you could do, but you're definitely taking a wrong route here.

The junior-level solution here would be something like what you're doing, but returning the actual promise and then in the rule action you don't just blindly fire the server call. Instead, you carefully resolve your promise there and add the s.t() or s.tl() in the promise resolvation callback.

The mid-level would be probably something like deploying a listener to the library load from the s code or from a page top rule, and then in that listener's callback running a _satellite.track() or something like that to trigger the rules that rely on that lib. Basically what @trincot suggested.

The senior-level solution is actually the simplest, but it's also the best practice: make your front-end dev team defer the pageview dataLayer event (yes, EDDL is presumed here) until the library is loaded (in good implementation it's just a matter of adding one promise into an array of promises analytics should wait for)

But really, I'm giving these solutions as wildcards. They will work, but most likely, your whole approach should be changed completely to make the solution perfect. But for that, of course, we'd need to know what exactly you're trying to wait for and why.

This sounds to me like a consent-related issue where consent is governed by a third party library and this problem only affects the first pageview in a session since after that you're just supposed to use cookies and not the library to gather existing consent. It's typical and in these cases we normally have the consent action triggering the pageview when the consent doesn't exist on the page load, so no need to wait for that lib to load whatever you do.