Wait for async JavaScript function with promise

JavaScript is asynchronous and this enables us to create websites that can respond to user interaction while doing other work, a non-freezing experience. An asynchronous behaviour can be a challenge when you want to make sure that functions is excecuted in a certain order or when you only want to execute a function if the previous call has finished.

You can use promises and await in JavaScript to gain control over your code and to be able to wait for functions to finish their executions. A function that returns a promise can be awaited.

This code has been tested and is working with Google Chrome (75.0.3770.100), Mozilla Firefox (67.0.4) and Microsoft Edge (42.17134.1.0), without any polyfill. It works in Internet Explorer (11.829.17134.0) with polyfills for XMLHttpRequest and Promise. If you want to support older browsers, check out our post on transpilation and polyfilling of JavaScript.

Funtion to be awaited

The following JavaScript function are supposed to be called in intervals and it can be awaited, it is marked as async and returns a promise. This function returns resolve on success and reject on an error.

async function getLog(log_name)
{
    // Return a promise
    return new Promise((resolve, reject) => {

        // Create form data
        var form_data = new FormData();
        form_data.append('LogName', log_name);

        var xhr = new XMLHttpRequest();
        xhr.open('POST', 'https://mysite.com/api/getlog', true);
        xhr.onload = function () {

            // Check if the response is successful
            if (xhr.status === 200)
            {
                // Return a success response
                resolve(xhr.response);
            }
            else
            {
                // Return a reject response
                reject(xhr.status + " - " + xhr.statusText);
            }
        };
        xhr.onerror = function ()
        {
            // Return a reject response
            reject(xhr.status + " - " + xhr.statusText);
        };
        xhr.send(form_data);
    });

} // End of the getLog method

Wait for function to finish excecution

The following function will call the getLog method in intervals while waiting for a long running task to finish. The getLog method is called with setTimeout until the finished boolean is true. This code makes sure that we do not call getLog more times than necessary.

async function syncFortnox(method)
{
    // Check if a lock is taken
    if (fortnoxlock.value === 'true') {
        toastr['error']('Du måste vänta tills den pågående processen blir klar.');
        return;
    }

    // Hide containers
    var collection = document.getElementsByClassName('hideable');
    for (var i = 0; i < collection.length; i++) {
        annytab.effects.slideUp(collection[i], 500);
    }

    // Lock the process
    fortnoxlock.value = 'true';

    // Get a log window reference
    var container = document.getElementById('log-window');

    // Start a loading animation
    //$('#log-window').html('<div class="annytab-basic-loading-container"><i class="fas fa-spinner fa-pulse fa-4x fa-fw"></i><div class="annytab-basic-loading-text">Arbetar ...</div></div>');
    container.innerHTML = '<div style="color:#4CAF50;"><i class="fas fa-spinner fa-pulse fa-fw"></i> Arbetar ...</div>';

    // Get fortnox api values
    var nox_api_values = new Object();
    nox_api_values.AccessToken = document.getElementsByName('txtAccessToken')[0].value;
    nox_api_values.PriceList = document.getElementsByName('txtPriceList')[0].value;
    nox_api_values.PenaltyInterest = parseInt(document.getElementsByName('txtPenaltyInterest')[0].value) / 100;
    nox_api_values.SalesVatTypeSE = document.getElementsByName('selectSalesVatTypeSE')[0].value;
    nox_api_values.SalesAccountSE25 = document.getElementsByName('txtSalesAccountSE25')[0].value;
    nox_api_values.SalesAccountSE12 = document.getElementsByName('txtSalesAccountSE12')[0].value;
    nox_api_values.SalesAccountSE6 = document.getElementsByName('txtSalesAccountSE6')[0].value;
    nox_api_values.SalesAccountSE0 = document.getElementsByName('txtSalesAccountSE0')[0].value;
    nox_api_values.SalesAccountSEREVERSEDVAT = document.getElementsByName('txtSalesAccountSEREVERESEDVAT')[0].value;
    nox_api_values.SalesAccountEUVAT = document.getElementsByName('txtSalesAccountEUVAT')[0].value;
    nox_api_values.SalesAccountEUREVERSEDVAT = document.getElementsByName('txtSalesAccountEUREVERESEDVAT')[0].value;
    nox_api_values.SalesAccountEXPORT = document.getElementsByName('txtSalesAccountEXPORT')[0].value;
    nox_api_values.PurchaseAccount = document.getElementsByName('txtPurchaseAccount')[0].value;
    nox_api_values.StockArticle = (document.getElementsByName('cbStockArticle')[0].value === 'true');
    nox_api_values.StockAccount = document.getElementsByName('txtStockAccount')[0].value;
    nox_api_values.StockChangeAccount = document.getElementsByName('txtStockChangeAccount')[0].value;
    nox_api_values.OnlyAllowTrustedSenders = (document.getElementsByName('cbTrustedSenders')[0].value === 'true');

    // Get a guid
    var guid = getGuid();

    // Get the log file name
    var log_name = dox_api_values.ApiEmail + "/" + guid + ".log";

    // Get logs every second
    var finished = false;
    setTimeout(async function run()
    {
        var response = '';

        try
        {
            // Get log information and wait for a response
            response = await getLog(log_name);
        }
        catch (err)
        {
            console.log(err);
        }
                
        if (finished === true)
        {
            // Print to log window and return
            printToLogWindow(container, response, finished);
            return;
        }
        else
        {
            // Print to log window and run again
            printToLogWindow(container, response, finished);
            setTimeout(run, 500);
        }
                
    }, 1000);

    // Create form data
    var form_data = new FormData();
    form_data.append('Guid', guid);
    form_data.append('DoxservrApiValues', JSON.stringify(dox_api_values));
    form_data.append('FortnoxApiValues', JSON.stringify(nox_api_values));

    var xhr = new XMLHttpRequest();
    xhr.open('POST', 'https://mysite.com/api/' + method, true);
    xhr.onload = function () {

        // Check if the response is successful
        if (xhr.status === 200) {

            // Mark the process as finished
            finished = true;
            //clearTimeout(timeout);
        }
        else
        {
            // Output error information
            toastr['error'](xhr.status + " - " + xhr.statusText);
        }

        // Release the lock
        fortnoxlock.value = 'false';
    };
    xhr.send(form_data);

} // End of the syncFortnox method

Leave a Reply

Your email address will not be published. Required fields are marked *