0

I have written a class to capture a microservice accessed vi https. Several calls have been implemented in asynchronous methods which all work fine by themselves (here: step1(), step2(), step3()).

However I want to call a few of those in succession (run()) to implement certain workflows.

What I can not manage so far is that the stepX methods get called after another. Also I want to call run() and get a promise back so I can .then() when all is done. Also I want to implement some error handling there to catch all errors that might occur during the chain.

Here's a class that reduces my problems to the core:

class Foo {
  run() {
    return this.step1()
    .then(() => this.step2())
    .then(() => this.step3())
  }

  step1() {
    console.log('1')
    return new Promise(function(resolve) { 
       setTimeout(function(){console.log('2')}, 500)
    });
  }

  step2() {
    console.log('3')
    return new Promise(function(resolve) { 
       setTimeout(function(){console.log('4')}, 500)
    });
  }

  step3() {
    console.log('5')
    return new Promise(function(resolve) { 
       setTimeout(function(){console.log('6')}, 500)
    });
  }
}

let bar = new Foo();

bar.run()
.then(console.log('7')) // react on run() finishing successfully
.catch(err => console.log('err:', err)) // global error handling

I expected this to output the numbers 1 to 7 in correct order. However the current output is

1
7
2

Thank you for any help! Christian

caefer
  • 63
  • 4
  • 3
    You're not `resolve`-ing a single promise. – Robby Cornelissen Mar 01 '21 at 06:25
  • 3
    In addition to what @RobbyCornelissen said: `.then(console.log('7'))` should be `.then(() => console.log('7'))` otherwise you just immediately execute the `console.log` and pass its return value (`undefined`) to `.then()` – VLAZ Mar 01 '21 at 06:28
  • Maybe also relevant [see here](https://stackoverflow.com/q/66275674) if you want a a fluent async API for the object, instead of returning promises. You'd be able to call `obj.step1().step2().step3()` with it. – VLAZ Mar 01 '21 at 06:31
  • I guess the best way to implement promises for very complicated flows would be to use ```async-await``` with ```try-catch-finally``` rather than using ```then-catch``` approach. This method provides a cleaner code than conventional ```then-catch``` – Srinath Kamath Mar 01 '21 at 06:37

2 Answers2

2

There were a couple problems in your code:

  1. You weren't resolving the promises in step1(), step2() and step3().
  2. .then(console.log('7')) was calling the console.log() immediately and not waiting for the promise. You must pass a callback to .then().

With these two issues fixed, here's the resulting code which you can run in the snippet and see the desired output:

class Foo {
    run() {
        return this.step1()
            .then(() => this.step2())
            .then(() => this.step3())
    }

    step1() {
        console.log('1')
        return new Promise(function(resolve) {
            setTimeout(function() { 
                console.log('2');
                resolve(); 
            }, 500)
        });
    }

    step2() {
        console.log('3')
        return new Promise(function(resolve) {
            setTimeout(function() { 
                console.log('4');
                resolve(); 
            }, 500)
        });
    }

    step3() {
        console.log('5')
        return new Promise(function(resolve) {
            setTimeout(function() { 
                console.log('6');
                resolve(); 
            }, 500)
        });
    }
}

let bar = new Foo();

bar.run()
    .then(() => console.log('7')) // react on run() finishing successfully
    .catch(err => console.log('err:', err)) // global error handling

This generates the output:

1
2
3
4
5
6
7
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • one more thing. I just replace resolve() in step2() with Promise.reject('my reason') and would've expected it to end up in the final .catch() but it doesn't? – caefer Mar 01 '21 at 07:21
  • 1
    @caefer - Calling `Promise.reject('my reason')` by itself just creates a new rejected promise that isn't chained to anything else. That has nothing at all to do with the `new Promise()` that was created and is being returned from the `stepX()` function. To reject the current promise, declare the 2nd argument as in `new Promise(function(resolve, reject) { ... });` and call `reject(new Error('my reason'))` instead of calling `resolve()`. – jfriend00 Mar 01 '21 at 07:29
0

You can use async-await approach with try-catch-finally to handle the resolve & reject cases in promises. I'm writing tweaking the code a little bit to write the example by using a static function. you can use it as you normally would.

class Foo {
  static async run() {
    let response = {status:false, result:'Failed' }
    try{
        const step1_response = await step1();
        const step2_response = await step2();
        response.status = true;
        response.result = {step1_response, step2_response};
    } catch(error) {
        console.log(error)
    } finally {
       return response;
    }
  }
}
Srinath Kamath
  • 542
  • 1
  • 6
  • 17