JavaScript är asynkront och det gör det möjligt för oss att skapa webbplatser som kan svara på interaktion samtidigt som annat arbete utförs, en responsiv upplevelse. Ett asynkront beteende kan vara en utmaning när du vill se till att funktioner körs i en viss ordning eller när du bara vill köra en funktion om den föregående körningen är klar.
Du kan använda löften (promises) och await i JavaScript för att få kontroll över din kod och för att kunna vänta på att funktioner körs klart. En funktion som returnerar ett löfte (promise) kan man vänta på.
Den här koden har testats och fungerar med Google Chrome (75.0.3770.100), Mozilla Firefox (67.0.4), Microsoft Edge (42.17134.1.0), detta utan någon polyfill. Koden fungerar i Internet Explorer (11.829.17134.0) med polyfills för XMLHttpRequest och Promise. Om du vill stödja äldre webbläsare kan du läsa vårt inlägg om transpilering och komplettering av JavaScript.
Funktion som ska inväntas
Följande JavaScript-funktion skall anropas i intervaller, den är markerad som async och returnerar ett löfte. Den här funktionen returnerar resolve om anropet lyckades och reject om något gått fel.
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
Vänta på att funktion ska slutföra körningen
Följande funktion anropar getLog-metoden i intervaller medan vi väntar på att en tidskrävande uppgift skall slutföras. Metoden getLog anropas med setTimeout tills det booleska värdet för finished är true. Den här koden ser till att vi inte anropar getLog flera gånger än nödvändigt.
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