Search code examples
androidnode.jspromisebluebird

Actions are out of order, when using bluebird


Full code: https://github.com/kenpeter/clip_to_music

Basically, this simple nodejs script should execute these actions in order

  1. Rename all mp4 file, so no files containing no space.

  2. Convert all mp4 to mp3

  3. kill adb server (with child process exec)

  4. start adb server (with child process exec)

  5. push to my android phone

Some sample code:

renamePromise
  .then(() => {
    return musicPromise;
  })
  .then(() => {
    return adbKillPromise;
  })
  .then(() => {
    return adbStartPromise;
  })
  .then(() => {
    return adbPushPromise;
  })
  .then(() => {
    console.log('---- all done----');
    process.exit(0);
  });

The output is like this, out of order

; yarn start
yarn start v0.21.3
$ node clip_to_music.js 

---adb kill---
-- Rename one file --
/home/kenpeter/Videos/4K Video Downloader/1.mp4
-- Rename one file --
/home/kenpeter/Videos/4K Video Downloader/2.mp4
--- rename all files done ---
adb push /var/www/html/test/testme/clip_to_music/audio/1.mp3 /sdcard/Music
adb push /var/www/html/test/testme/clip_to_music/audio/2.mp3 /sdcard/Music
---- done push all music ---
* daemon not running. starting it now on port 5037 *
* daemon started successfully *

---adb start---
single mp3 done!

single mp3 done!

--------- all mp3 conversion done --------
---- all done----
Done in 10.79s.

As you can see the output above, which is out of orders

  1. It kills the adb server (with cild process exec)
  2. Rename all mp4 file, so no files containing no space.
  3. push all music to my android phone
  4. start adb server (with child process exec)
  5. Convert all mp4 to mp3

Solution

  • Your code implies that you've already started the operations represented by musicPromise, adbKillPromise, adbStartPromise and adbPushPromise and you are running them all in flight at the same time. As such, they will finish in whatever order they naturally finish in and you have no direct control over that.

    Remember a promise is merely a tool for monitoring an async operation. Since you already have the promise, you must have already started the async operation and thus your chain of promises does not control the order of the activities at all. It does control the order in which you process the results, but not the order of the activities themselves at all.

    To truly sequence the operations themselves, you need to not start the succeeding operations until the ones you want to precede it are done more like this:

    a().then(b).then(c).catch(err => { /* handler error here */});
    

    Where a(), b() and c() are functions that return a promise and each start their corresponding async operation.

    As an example with one of your operations, change this:

    // adb kill
    var adbKillPromise = new Promise((resolve, reject) => {
      exec("adb kill-server", (err, stdout, stderr) => {
        if (err) {
          console.error(err);
          return;
        }
    
        console.log(stdout);
        console.log('---adb kill---');
        resolve();
      });
    });
    

    to be a function that you can call later:

      // adb kill
      function adbKill() {
        return new Promise((resolve, reject) => {
          exec("adb kill-server", (err, stdout, stderr) => {
            if (err) {
              console.error(err);
              reject(err);
              return;
            }
    
            console.log(stdout);
            console.log('---adb kill---');
            resolve();
          });
        });
      }
    

    Note, that it is now a function that returns a promise so it can be called upon demand (only when it is time to call it) and I also added error handling to it so it rejects if there's an error.

    And, do that for all the other variables you have defined that correspond to one of the async operations you want to sequence. Then, you can do this:

    rename().then(music).then(adbKill).then(adbStart).then(adbPush).then(() => {
        console.log('---- all done----');
        process.exit(0);
    }).catch(err => {
        console.log('Error', err);
        process.exit(1);
    });