david warne
david warne

Reputation: 127

Class with common callback in javascript

i want to build a javascript class to register several different functions to execute a single common callback, all registered functions should execute asynchronously and then once all of them are finished, it should execute the defined callback function.

We should also be able to define a maximum time before the callback function executes. For example if we define that as 3000 and it takes more than 3000 ms for all registered functions to return, it should proceed to execute callback function even though the ajax functions have not finished retuning.

To be clear, this code needs to be flexible, standalone and reusable

To assume that any function that we register will implement a call to a function that we define at some point within it mark its completion. for eg. at the end of my function i'll enter myClass.markDone() to let the class know the function has completed executing

Is it possible using javascript or with angular.js and without jquery?

Upvotes: 0

Views: 175

Answers (3)

Máté Safranka
Máté Safranka

Reputation: 4106

I asked for clarification in a comment already, but I went ahead and started drafting a solution. Here's what I came up with, hope this is what you meant:

var AsyncBatch = function() {
    this.runnables = new Set();
};

AsyncBatch.prototype = {
    runnables: null,
    timeoutId: 0,

    add: function(runnable) {
        this.runnables.add(runnable);
    },

    start: function() {
        this.timeoutId = window.setTimeout(this.timeout.bind(this), 3000);

        let promises = [];
        for (let runnable of this.runnables) {
            promises.add(new Promise(resolve => {
                runnable(resolve);
            }));
        }
        Promise.all(promises).then(() => this.allDone());
    },

    allDone: function() {
        if (this.timeoutId == 0) return;
        window.clearTimeout(this.timeoutId);
        this.finish();
    },

    timeout: function() {
        this.timeoutId = 0;
        this.finish();
    },

    finish: function() {
        // Do something here when all registered callbacks are finished OR the batch timed out
    },

};

Here's how you would use this:

  1. Create an instance of AsyncBatch.
  2. Call .add() as many times as you want, passing a function. This functions should expect a single parameter, which is the callback it should invoke when its job is done.
  3. Call .start() on the AsyncBatch instance. It will execute all runnables asynchronously, as well as start a timer.
  4. If the runnables all finish before the timer runs out, .allDone will cancel the timer and execute .finish().
  5. If the timer runs out before the runnables, it executes .finish(), and sets the timerId to 0 so that .finish() won't be called again when the runnables all finish.

Upvotes: 0

jdachtera
jdachtera

Reputation: 759

To achieve this take a look at these angular built in modules:

https://docs.angularjs.org/api/ng/service/$q

https://docs.angularjs.org/api/ng/service/$timeout

Here is an example implementation on plunkr:

qAllWithTimeout([
    makePromise(function(callback) {
      // Mock async call 1
      setTimeout(callback, 200);
    }),
    makePromise(function(callback) {
      // Mock async call 2
      setTimeout(callback, 500);
    }),
    makePromise(function(callback) {
      // Long running mock async call 2
      setTimeout(callback, 10500);
    })
  ], 3000)
    .then(function() {        
        $scope.state = 'ready';
    })

http://plnkr.co/edit/hNo9kJmKIR4hEoNk9pP2?p=preview

Upvotes: 1

Gilson PJ
Gilson PJ

Reputation: 3580

iam not sure this will work, am not tested. this may give you some idea.

function TestFun(callback, timeout){
    this._callback = callback;
    this._timeout = timeout;
    this._forceFinish = false;
    this.fun_list = [];
    this.RegisterFun = function(fun){
        this.fun_list.push(fun);
    };

    this.startCount = -1;
    this.finishCount = 0;

    this.timeOutFunction= function(){
        this.startCount++;
        fun_list[this.startCount]();
        this.commonCallback();
    }
    this.Start = function(){
        for(var i=0; i <this.fun_list.length ;i++){
            setTimeout( this.timeOutFunction, 0);
        }
        setTimeout( this.watcherFun, 1 );
    };

    this.commonCallback = function(){
        if( this._forceFinish){
            this._callback();
        }else{
            this.finishCount++;
            if(this.finishCount == this.fun_list.length ){
                this._callback();
            }
        }
    }

    this.watcherFun = function(){
        if( this._timeout !=0 ){
            this._timeout-1;
            setTimeout( this.watcherFun, 1 );
        }else{
            this._forceFinish = true;
            this.commonCallback();
        }
    }
}

//usage

var funMngr = new TestFun(finalFun, 60 * 1000);
funMngr.RegisterFun ( fun1 );
funMngr.RegisterFun ( fun2 );
funMngr.RegisterFun ( fun3 );
funMngr.Start();

function finalFun(){
    alert("all functions executed" );
}

Upvotes: 0

Related Questions