Search code examples
angulartypescriptes6-promise

Typescript Execute Promises Sequentially


I'm having a hard time getting a chain of promises in a for loop to execute sequentially. I have an array and 5 functions that all return promises. For each element in the array, i'm creating 5 parameters and each parameter goes into each 5 functions which need to be executed sequentially before proceeding to the next item in the array. I need to promises to execute in the following manner.

  • item 1
    • func1(param1)
    • If func1 succeeds continue to func2 else log errors and stop.
    • If func2 succeeds continue to func3 else log errors ...
  • item 2 ....

and so on.

    const array = ['a','b','c','d','e']

    evalParam(param,callback){
        //do things with param 
        callback(true);

    func1(param) {
        return new Promise((resolve, reject) =>{
            this.evalParam(param, resolve);
        });
    }
         .
         .
         .
    func5(param) {
        return new Promise((resolve, reject) =>{
             this.evalParam(param, resolve);
         });
    }
    lastFunc(errors)
    {
       //Do things with errors
       console.log(errors)
    }

    executeChain(array){
        errors : any[] = []
        return new Promise(resolve,reject) => {
            return new Promise(resolve,reject) => {
                for (let item of array){
                    const param1 = this.createParam1(item)
                    const param2 = this.createParam2(item)
                    const param3 = this.createParam3(item)
                    const param4 = this.createParam4(item)
                    const param5 = this.createParam5(item)
                    Promise.resolve()
                        .then(() => { 
                              func1(param1) }
                        .then((val) => {
                            if (val) {
                                func2(param2)
                            } else {
                                errors.push("func1 failed with param: " + param1)
                            }
                               .
                               .
                               .
                        .then((val) => {
                            if (val){
                                func5(param5)
                            else {
                                errors.push("func5 failed with param: " + param5)
                            }
                         })
                } // End for loop
           .then(()=> {
                resolve(errors)
           })
  }

Any help would be appreciated.


Solution

  • I would suggest few things:

    • Separate your code into smaller functions
    • Start using async/await which works perfectly for such cases and makes your code much clearer

    The solution will look similar to:

    const paramHandlers: Function[] = [
        // func1
        () => {},
        // func2
        () => {}
        // ...
    ];
    
    async function executeChain(array: any[]): Promise<any> {
        const errors = []; 
        for (const i in array) {
            try {
                // wait for item to be processed or failed with error
                // and continue 
                await executeItem(array[i]);
            } catch (error) {
                // one of param handlers failed with 'error'
                errors.push(error);
            }
        }
        return errors;
    }
    
    async function executeItem(arrayValue: any): Promise<any> {
        const params = createParams(arrayValue);
        for (const i in params) {
            const param = params[i];
            // lookup corresponding handler by index
            const paramHandler = paramHandlers[i];
            // execute function and wait for promise to resolve
            // for loop will not continue to next execution until promise resolved
            // if paramHandler's promise will be rejected, 
            // then executeItem will return rejected Promise, having same error
            await paramHandler.apply(param);
        }
    }
    
    function createParams(value: any): any[] {
        return [
            /*
             createParam1(value),
             createParam2(value),
             ...
            */
        ];
    }
    

    If you still want to use promises, then executeItem can look as following:

    function executeItem(arrayValue: any): Promise<any> {
        const params = createParams(arrayValue);
        return params.reduce((result, param, i) => {
            const paramHandler = paramHandlers[i];
            return result.then(() => paramHandler.apply(param));
        }, Promise.resolve());
    }
    

    you can do similar stuff for executeChain.

    Please also check this question. It contains extensive info on sequential promises processing.