0

I posted a similar question several days ago but I have made some changes and commenting on that question was becoming tedious, so it was recommended I ask a new question. The idea is that I want to execute four equations synchronously. Inside those equations are HTTP requests. I have two of the equations working properly and but there is one equation that involves two POST requests and a GET requests. The second requests relies on the first and the third request relies on the second.

I have tried several different methods to get this to work. I have tried flattening my promises, returning the promises. All kinds of things, with no luck. I am not sure where I am going wrong.

Synchronous code snippet:

this.getData1(user, userID).then(() =>
{
    this.getData2(user, userID)
        .then(() =>
        {
            this.getData3(user, lan).then(() =>
            {
                this.userCheck(user);
            })

        });
});

I have getData2 and getData3 working.

getData1 looks like:

getData1(user: string, id: string){
    console.log('grabbing CC information', id, user);
    return new Promise((resolve, reject) =>
        {
    var status: string;
    this._apiService.getAssertion(id).subscribe((data: any) =>
    {
        let assert = data.toString();
        this._apiService.getToken(assert).subscribe((data: any) =>
        {
            let tkn = data.access_token.toString();

            this._apiService.ccMeta(tkn, guid).subscribe((data: any) =>
            {
                parseString(data, (err, result) =>
                {
                    if (err)
                    {
                        throw new Error(err);
                    }
                    else
                    {
                        status = result['entry']['content'][0]['m:properties'][0]['d:status'][0];
                        this.ccData.push(
                        {
                            key: 'userStatus',
                            value: status
                        })
                    }
                });
            });
        });
    });
    resolve()
        });
}

I also tried something like this previously. It did not work either.

apiService.getAssertion(id).then(assert =>
{
    return apiService.getToken(assert.toString(), user);
}).then(data =>
{
    return apiService.ccMeta(data.access_token.toString(), id);
}).then(parseStringPromise).then(information =>
{
    this.ccData.push(
    {
        key: 'userStatus',
        value: information.entry
    });
});

Inside this function the getAssertion function is a POST request. The getToken function is another POST request that relies on the assertion from the first POST request. Finally, ccMeta is a get request that relies on the token from the second POST request.

I would expect getData1 to execute first, then getData2, then getData3, and finally, userCheck. Inside getData1 I need the assertion, then the token, and then get request to execute synchronously. The code snippet above is not executing correctly. The assertion is not properly being used in the getToken equation.

I would greatly appreciate some help.

  • Sequentially, not synchronously. Promises are always asynchronous :-) – Bergi Jun 23 '19 at 18:18
  • You're still using the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it), and calling `resolve()` in the wrong place. – Bergi Jun 23 '19 at 18:19
  • I have done all of things you have recommended in the last post. I made an edit to add in one of the things I tried. @Bergi – new_programmer_22 Jun 23 '19 at 18:36
  • Is the first line ```New Promise``` the anitpattern? I thought I had to set it up that way in order to execute the four functions in order. That was recommended to me by someone else. @Bergi – new_programmer_22 Jun 23 '19 at 18:42
  • That looks like the code I posted in my comment, not what you should try in your environment. You would of course need to use `this._apiService` instead of `apiService`, provide the promisified version of `parseString` etc. – Bergi Jun 23 '19 at 18:44
  • Yes, given that your api service returns promises (or things that can readily be converted into promises), using `new Promise` is an antipattern here. Where was this suggested? – Bergi Jun 23 '19 at 18:45
  • I think I found the recommendation in another post here on Stackoverflow. It was not my question specifically. I cannot find the exact question – new_programmer_22 Jun 23 '19 at 19:00

1 Answers1

2

Since these HTTP calls are in fact observables and not promises, I think you should look into observable composition using pipe and switchMap for instance. If you still want you method to return a promise, it could look like this:

getData1(user: string, id: string) {
  console.log('grabbing CC information', id, user);

  return new Promise((resolve, reject) => {
    this._apiService.getAssertion(id)
      .pipe(
        switchMap((data: any) => {
          let assert = data.toString();
          return this._apiService.getToken(assert);
        }),
        switchMap((data: any) => {
          let tkn = data.access_token.toString();
          return this._apiService.ccMeta(tkn, guid);
        }),
      )
      .subscribe(
        data => {
          parseString(data, (err, result) => {
            if (err) {
              reject(new Error(err));
              return;
            }

            const status: string = result['entry']['content'][0]['m:properties'][0]['d:status'][0];
            this.ccData.push({
              key: 'userStatus',
              value: status
            });
            resolve();
          });
        },
      );
    });
}
ManuKpL
  • 231
  • 1
  • 5
  • 1
    Isn't there a simple `toPromise()` method on observables, so that you can avoid the `new Promise` constructor? – Bergi Jun 23 '19 at 18:20
  • Yes. There is. I have used .toPromise.then(). Did not have luck with that either @ Bergi – new_programmer_22 Jun 23 '19 at 18:28
  • @Bergi that was my first thought too but since we needed first to push data to `this.ccData` I felt it would be cleaner to explicitly do it in a subscribe. But the same result could be achieved with a `tap` for example as last operator inside the pipe before returning the result of toPromise. – ManuKpL Jun 23 '19 at 21:32
  • @ManuKpl I tried your suggestion but I get an error for the third call to the api service. ``` Type 'void' is not assignable to type 'ObservableInput'.```. Any ideas why? – new_programmer_22 Jun 24 '19 at 12:55
  • @newwebdev22 my bad! I actually forgot to take into account the `parseString` method. Since it returns void, it doesn't matter to return values from its callback. I updated my answer and wrapped it in an Observable. You may want to refactor this a bit but it should now work. – ManuKpL Jun 25 '19 at 07:19
  • @newwebdev22 I did some refactor myself because it bothered me :D – ManuKpL Jun 25 '19 at 07:26
  • I will try this as soon as I am out of my meeting! I will let you know how it plays out. – new_programmer_22 Jun 25 '19 at 12:24