Top community members

Welcome to Dirask IT community! ❀ đŸ’»
We are community of people that helps each other.

If you are beginner in IT field, you are more then welcome to ask questions, it will help you to learn faster. We are here to help you.

We are always beginner in something, we just need to remember it along the way.

there are no wrong questions - Ask Question

[Edit]
EN

JavaScript - how to make buffered async loop class with next iteration confirmation?

4 points
Created by:
Greg
1443

In this article, we're going to have a look at how to write own asynchronous buffered loop class  (window loop class) that uses callback functions to execute next iterations in proper time or breaks loop - processes asynchronous tasks as resources are available.

This kind of loop is useful when we have to make some iterations and shedule them in groups, e.g. optimisation that has to execute 50 computations but available are only 5 machines and we don't want to make new interations until machine is avaialble.

Note: read this article to see simple async loop with next iteration confirmation example.

1. Custom class example

This implementation uses setTimeout function to call iterations in proper time. To be sure that next iteration will be executed it is necessary to call resume() method in each onIteration method. To break loop it is just necessary to call finish() method.

Note: in below example we shedule next iterations after 1s and break loop after 10 iterations to show how it works.

// ONLINE-RUNNER:browser;

function AsyncLoop(index, count, buffer, onIteration, onFinished) {
    var STOPPED = 0; // loop is stopped
    var STARTED = 1; // loop is started
    var AVAILABLE = 2; // loop is started and ready to execute next iterations
    var ITERATING = 3; // loop is progressing iterations
    var FINISHING = 4; // loop is finishing

    var self = this;

    var i, c, b; // executed indexes, confirmed indexs, free buffer
    var state = STOPPED;

    function shedule(i) {
        var called = false;

        var cover = function(action) {
            return function() {
                if (called) {
                    throw new Error('Only once callback function can be executed in iteration.');
                }
                called = true;

                c -= 1;
                b += 1;

              	if (state == ITERATING) {
                	state = AVAILABLE;
                }

                action();
            };
        }; 

        var callback = function() {
            if (state == FINISHING) {
                return;
            }

            onIteration(i, cover(resume), cover(finish));
        };

        setTimeout(callback, 0);
    }

    function resume() {
        if (state == STARTED || state == AVAILABLE) {
            if (i < count) {
                state = ITERATING;

                while(i < count && b > 0) {
                    i += 1;
                    b -= 1;

                    shedule(i - 1);
                }
            } else {
                if (c == 0) {
                    state = FINISHING;

                    var callback = function() {
                        state = STOPPED;

                        onFinished(true);
                    };

                    setTimeout(callback, 0);
                }
            }
        }
    }

    function finish() {
        if (state == STARTED || state == AVAILABLE || state == ITERATING) {
            state = FINISHING;

            var callback = function() {
                state = STOPPED;

                onFinished(false);
            };

            setTimeout(callback, 0);
        }
    }

    self.run = function() {
        if (state == STOPPED) {
            i = index;
            c = count - index;
            b = buffer;

            if (onIteration.length > 1) {
                // for onIteration(index, resume, finish)
                state = STARTED;

                resume();
            } else {
                // for onIteration(index)
                state = STARTED;

                try {
                    while(i < count) {
                        i += 1;
                        onIteration(i - 1);
                    }
                } finally {
                    state = STOPPED;

                    if (onFinished) {
                        onFinished(true);
                    }
                }
            }
        }
    };
  
  	self.end = finish;
}

// Helper logic

var t1 = new Date();

function getTime() {
    var t2 = new Date();

    return t2 - t1;
}

// Usage example

function onIteration(i, resume, finish) {
    console.log('[loop iteration ' + i + ', time=' + getTime() + ']');

    // resume() call continues loop
    // finish() call breaks loop

    if (i == 10) {
        console.log('finish() method called...');
        finish();
    } else {
        setTimeout(resume, 1000); // continued after 1s
    }
}

function onFinished(completed) {
    console.log('[loop finished, time=' +  getTime() + '] ' + (completed ? 'completed' : 'forced'));
}

var initialIndex = 0;     // initial iteration number
var iterationsCount = 50; // number of iterations to execute
var bufferSize = 3;       // maximum number of executed iterations in same time

var loop = new AsyncLoop(initialIndex, iterationsCount, bufferSize, onIteration, onFinished);

loop.run();
console.log('[loop started, time=' + getTime() + ']');

 

Checkout latest Findings & News:

Checkout latest questions:

Checkout latest wiki articles:

Hey 👋
Would you like to know what we do?
  • Dirask is IT community, where we share coding knowledge and help each other to solve coding problems.
  • We welcome everyone,
    no matter what the experience,
    no matter how basic the question is,
    this community will help you.
Read more