In the following example, I am simulating a scenario where an user can trigger some actions, every action is a asynchronous operation (wrapped in a promise) which can be resolved with a random timing.
The list of actions is also dynamic, an user can trigger only one actions or many in a span of time.
I would need:
I can use ES6 and Browser native Promises
To test the example, click several times (with variable frequency) the button.
(function (window) {
document.addEventListener('DOMContentLoaded', e => {
let logActionsElm = document.getElementById('logActions');
let logOperationsElm = document.getElementById('logOperations');
let logCounterElm = document.getElementById('logCounter');
let btnActionElm = document.getElementById('btnAction');
let actionCounter = 0;
let operationDurations = [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000];
let getRandomNumber = (min, max) => Math.floor(Math.random() * (max - 0 + min)) + min;
let getRandomOperationDuration = x => operationDurations[getRandomNumber(0, 8)];
let promises = [];
let createAction = (id, duration) => {
logActionsElm.innerHTML += `${id} action_start_duration: ${duration} --------- ${id}<br>`;
let promiseAction = new Promise((resolve, reject) => {
setTimeout(e => {
logActionsElm.innerHTML += `${id} action_end___duration: ${duration} --------- ${id}<br>`;
}, duration);
});
};
let createOperation = x => {
actionCounter++;
let duration = getRandomOperationDuration() / 10;
createAction(actionCounter, duration);
//logActionsElm.innerHTML += `action ${actionCounter} created will resolve after ${duration}<br>`;
};
btnActionElm.addEventListener('click', e => {
createOperation();
});
var counter = 0;
setInterval(x => {
if (counter >= 20) {
return;
}
counter++;
logCounterElm.innerHTML += `${counter} second <br>`;
}, 1000);
});
})(window);
body {
font-size: 1.5em;
font-family: 'Courier New';
}
#logCounter {
position: fixed;
top: 0;
right: 0;
margin: 2em;
}
<button id="btnAction">Triger and action</button>
<div id="logActions"></div>
<div id="logOperations"></div>
<div id="logCounter"></div>
You could append new action functions to a single promise to ensure actions aren't evaluated until after all previous actions have resolved:
let queue = Promise.resolve();
let userActionIdCounter = 0;
function queueAction(fn) {
queue = queue.then(fn);
return queue;
}
function userAction() {
return new Promise((resolve) => {
const seconds = Math.ceil(Math.random() * 5);
const actionId = userActionIdCounter;
userActionIdCounter += 1;
console.log('User action', userActionIdCounter, 'started');
setTimeout(() => {
console.log('User action', userActionIdCounter, 'complete');
resolve();
}, seconds * 1000);
});
}
document.getElementById('action').addEventListener('click', () => {
queueAction(userAction);
});
<button id='action'>Trigger action</button>