Wait for pending runs to finish to proceed, Javascript

Asked

Viewed 1,555 times

2

That was the most practical example I could find.. is an example where the server sends 2 messages (or more) in a row and at "the same time", it does not check if there is something running, in case if there was it should wait until the end to send the new request.

I thought about creating a finite loop that waits for a variable to stay false to proceed, the variable would be the one that says that the work of the last request "ended" but I think this is gambiarra.

    // CONFIGURACOES DA DB //
var db = new Dexie('teste');

db.version(1).stores({
    sequences: '++id,of'
});
db.open().
catch ();
// CONFIGURACOES DA DB //

var executionOrder = []; // tem como deixar vazio sem ter undefined?

// isso foi o exemplo mais pratico que encontrei.. é um exemplo em que o servidor envia 4 mensagens (ou mais) seguidas e ao "mesmo tempo", ele nao checa se há algo sendo executado, no caso se houvesse ele deveria esperar até o fim para enviar a nova requisiçao

setInterval(function () {
    var newOrder = ['NOVO'].reverse();
    executionOrder = executionOrder.concat(newOrder);
    execute('INIT');
}, 4000);

setInterval(function () {
    var newOrder = ['APAGA'].reverse();
    executionOrder = executionOrder.concat(newOrder);
    execute('INIT');
}, 4000);

setInterval(function () {
    var newOrder = ['NOVO'].reverse();
    executionOrder = executionOrder.concat(newOrder);
    execute('INIT');
}, 4000);

function execute(req) {
    if (req == 'INIT') {
        callback();
    }  if (req == 'NOVO') {
        db.sequences.add({
            of: '0'
        });
        console.log('ADD 1');
        callback();
    }  if (req == 'APAGA') {
        db.sequences.where("of").equalsIgnoreCase('0').each(function (seq) {
            db.sequences.delete(seq.id);
        }).then(function () {
            console.log('DEL ALL');
            callback();
        });
    }
};

function callback() {
    if (executionOrder.length) {
        var next = executionOrder.pop();
        execute(next);
    }
}

if you take a setInterval will observe that everything goes well.. But with 2 or more it does not wait..

Working example: http://jsfiddle.net/uxnry1m6/

  • Please contextualize your question. Does this run on the client? Nodejs Server? Requests are coming from multiple clients? Single client? Websocket?

  • @Viníciusgobboa.deOliveira this runs on the client, the setinterval represent messages coming from the Websocket server that is handled by OnMessage which is represented by Setinterval. The execute function is who handles incoming messages.

1 answer

3


The general idea of using a variable as "semaphore" is OK (but using it in a loop would be even gambiarra, and would not work), and the way your code is structured can be done with a minimum of effort:

  1. Create variable, initially allowing any execution:

    var semaforo = true; // Aberto
    
  2. When you are going to run something time consuming and asynchronous, close the traffic light:

    if (req == 'APAGA') {
        semaforo = false; // Fechado
        db.sequences.where("of").equalsIgnoreCase('0').each(function (seq) {
            db.sequences.delete(seq.id);
        }).then(function () {
            console.log('DEL ALL');
            semaforo = true; // A execução demorada acabou, aberto de novo
            callback();
        });
    }
    

    Don’t forget to do this for every asynchronous operation by closing the traffic light before of it to begin (and not in the middle).

  3. Finally, before performing anything check if the traffic light is open:

    function callback() {
        if (semaforo && executionOrder.length) {
    

    If it is not, it does not perform. But then when will it perform? Simple: what do you call the callback at the end of each run, it will check again if there are pending operations and do them.

In your case, that’s enough. But to make everything more robust, I suggest you modify the callback to perform not only one, but all pending actions, one at a time (don’t worry about the competition, the traffic light ensures that each operation waits for the last to be completed):

function callback() {
    while (semaforo && executionOrder.length) {
        var next = executionOrder.pop();
        execute(next);
    }
}

P.S. I also found a problem in how you are implementing a queue, reversing a list and using it as stack works well - as long as you do not add more elements to it. Because if you do, those elements will go to the front of the line, not the end, messing up the order. Instead, I suggest using the list as a queue, and not as a stack, calling portantoshiftem vez depop`:

var newOrder = ['NOVO']; // Sem o .reverse()
executionOrder = executionOrder.concat(newOrder);

...

var next = executionOrder.shift(); // Em vez de .pop()

Full example.

  • I tried to apply and I couldn’t, I keep trying

  • @Elaine You applied this principle also in the case NOVO? (for the method add by the way it is also asynchronous, if you do not use the semaphore in it another operation can start before this ends) Or is your actual code very different from the question? (may be trouble somewhere else)

  • See I’ve applied to everything.. http://jsfiddle.net/uxnry1m6/3/ still doesn’t work, this code is my real code.

  • @Elaine the ADD was applied wrong (the call of the callback was outside the then). Also, if you want to implement a queue, flip the list and treat it like a stack only works once - if you add more elements to it, the order goes crazy. Instead, use the concat in the same direct order, and in place of pop use shift. I updated the example with these changes, see if there are any problems left: http://jsfiddle.net/uxnry1m6/4/

  • By the way, I noticed you wear one each and after him a then, this can be problematic (I’m not sure, because I have no experience with this Dexie library), because it seems to me that the then is running once for each deleted element (and not once after all elements are deleted). Please confirm this, and if so I can update my reply with a suggestion to that effect.

  • What a mess of mine, I’m sorry, your script works but in the aspects, but in another it fails. See, the then is executed only when the loop is completed, ie it is only called once for each request.

  • What a mess of mine, I’m sorry, your script works but in the aspects, but in another it fails. See, the then is executed only when the loop completes, ie it is only called once for each request. The remaining problem is: he is grouping the executing strokes! for example this sequence: ['NOVO', 'NOVO', 'APAGA', 'NOVO', 'APAGA'] he groups the 3 NOVOS and executes them first and groups the 2 APAGA and executes them in a group after the NOVOS

  • @Elaine I’ll try to reproduce this, just a moment...

  • The fact that it groups cannot be ignored because it disturbs my other executions, let’s go to this example: if I put 1 in the equation of just sum, and then put the 2.. but then I decide to delete the numbers placed previously (1 e 2) and put the numbers 4 and 7 and carry out the equation the result would have to be 11 but if he groups the steels the result will be another that should not occur.. got it right?

  • @Elaine There was also a mistake in DEL, you were closing the traffic light inside the where and not before of it. Now I’m sure it works, see this most descriptive example. (and the real example)

Show 5 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.