1

I am new to JS/TS and thus Promises, I managed to write my "Steps" as promises from porting from C# but I will take whatever construct you can outline. (working with ionic2/TS).

Why Promises: I using Promises in TS in order to sequence a series of tasks during a playback of a slide in a collection of slides. (not sure I am using promises correctly, perhaps there is other constructs, patterns to consider for this, let me know)

My Goals: How do I construct a chain of promises such that:

  1. I can iterate over a chain of steps...
  2. I can repeat Step2() say 5 times?
  3. Recover from Step1() failure and continue with the next Index/Slide (all next steps).

Here is my current code outline

PlayAll() {

    Step1(Index)
        .then(() => {
            console.log("PlayAll<<promise------------Step1 DONE");
            Step2(i)
                .then(() => {
                    console.log("PlayAll<<promise------------Step2 DONE");
                    if (Index < Count) {
                        Index++;
                        // tell slider to move and wait for its change event
                    // ??? 
                    }
                    else
                        // -) Report we are done with all Slides
                        // Perhaps I should write PlayAll as a promise too?
                        // instead of using a Callback ??
                        if (typeof (this.Done) == "function")
                            this.Done(); // a registered callback.
                }
                .catch(() => {
                console.log("PlayAll<<promise------------Step2 FAIL");
                }

        .catch(() => {
                console.log("PlayAll<<promise------------Step1 FAIL");
        }
}

My Step1() looks like this

    // Using Howler.js (makes audio work on Android)
    // https://stackoverflow.com/questions/30069988/how-can-i-create-a-promise-for-the-end-of-playing-sound
    // 20160820
    PlayOne(Media: string) {
        console.log("AudioPlayList------------>>PlayOne Media is", Media);
        return new Promise((resolve, reject) => { // return a promise
            var audio = new Howl({ src: [Media] });                     // create audio wo/ src
            // cache a class level copy for other methods, eg. pause
            this.audio = audio;
            //
// TODO: needs help:
// not sure how to code reject here ???
// does this look correct?          
            // audio.on('error', () => {
            //     console.log("AudioPlayList<<e------------audio ERROR");
            //     reject(Media);
            //     return reject;
            // });      

            audio.on('end', () => {
                console.log("AudioPlayList<<e------------audio ENDED");
                resolve(Media);
                return resolve;
            });                     // when done, resolve
            audio.play();
        });
    }

My Step3() not shown in the above outline looks like this

timer1: any;  // needed to abort Slide's PostPause, if user alters playback

  // https://www.typescriptlang.org/docs/release-notes/typescript-1.7.html
  // 20160812
  private SlidePostPause() {
    console.log("PlayAll------------>>PostPause");
    return new Promise<void>(
      resolve => {
        this.timer1 = setTimeout(resolve, this.Model.PostPause * 1000);
      });
  }

This link How to return from a Promise's catch/then block

says _Returning a regular value from a reject handler, causes the next .then() resolve handler to be called (e.g. normal processing continues)_

which is what I typically want.

I have also read this link https://github.com/basarat/typescript-book/blob/master/docs/promise.md

but I am still lost :)

Please be as explicit as you can be.

Thank you for your help.

Community
  • 1
  • 1
Meryan
  • 1,285
  • 12
  • 25
  • Interesting. Perhaps you need a custom class with states to handle that. It would be messy IMO to force it into a promise chain. – unional Apr 01 '17 at 02:59
  • For example, you can take a look at this to see how states can be managed. It is only one why to do it. There are many other ways: https://github.com/unional/assert-order – unional Apr 01 '17 at 03:01
  • How about try `async`/`await`? It would be easier to write your retry/recover logic with it. – Haocheng Apr 01 '17 at 08:23
  • @Haocheng can you provide with an outline of what you in mind. Thank you. – Meryan Apr 01 '17 at 10:55

1 Answers1

0

Here is the code I have arrived to. I am able to repeat a chain of promises using a while loop. Recovering from a failed promise relies on a correct rejection, initially my sound playing using Howler.js was not registering the correct spelling of the event and caused weird behaviors in the nested promises this playback requires.

Everything is working as I intended expect that altering the while loop limit dynamically may not be supported in TypeScript so in the current behavior it appears that once the loop is entered altering the count does not take effect until the next slide is started thus the next while-loop is issued.

I think I can read this promise.then.catch six months from now and make sense of it :) I was not aware of async/await being supported in TypeScript I wonder how it may improve on this.

    //  
    this.PlaySlideRepetitions(Index).then(() => {
      console.log("AMSController<<promise------------PlaySlideRepetitions DONE ");
      //
      if (Index < this.AMSData.Count) {
        console.log("AMSController<<------------>> Play Next Slide");
        // Tell View to Update 
        // NOTE: this will cause the view SlideChanged() to call back in here PlayIndex()
        this.AMSView_I.SlideNext();
      }
      else {
        console.log("AMSController<<----------->> DONE playing all slides");
        this.Done();
      }
    });



  // 20170403
  PlaySlideRepetitions(Index: number) {
    let DynamicCount = this.GlobalService.AMSRepeatCount_;
    let chain = Promise.resolve({});  // trick to kick off this darn Promises :)
    var i = 0;
    while (i < DynamicCount) {
      chain = chain.then(() => {
        return this.PlaySlideOnce(Index).then(() => {
          console.log("AMSController<<promise------------PlaySlideOnce DONE " + i);
        })
          .catch(() => {
            console.log("AMSController<<promise------------PlaySlideOnce FAIL " + i);
          });
      });
      i++;
      DynamicCount = this.GlobalService.AMSRepeatCount_; // may be altered by user
    }
    return chain;
  }
  // 20170401
  PlaySlideOnce(Index: number) {
    console.log("AMSController------------>>PlaySlideOnce Index=", Index);
    return new Promise((resolve) => {
      // Step 1
      this.AudioPlayList.Play(this.AMSData.Audio(Index))
        .then(() => {
          console.log("AMSController<<promise------------AudioPlayList DONE");
          // Step 2  
          this.SlidePostPause()
            .then(() => {
              console.log("AMSController<<promise------------SlidePostPause DONE");
              resolve();
            })
            .catch(() => {
              console.log("AMSController<<promise------------SlidePostPause FAIL");
            });
        })
        .catch(() => {
          console.log("AMSController<<promise------------AudioPlayList FAIL");
          // return 123;
          // return Promise.resolve(123);
        });
    });
  }

Thanks for all the suggestions

Meryan
  • 1,285
  • 12
  • 25