1
\$\begingroup\$

I would like to write a nodejs IP driver for my AVR. In order to turn on my AVR and switch it to channel '321' i have to send key presses. A key press can be seen as a 'press key' and 'release key' command.

Basically, I have to write the following code:

  • press(turn_on)
  • delay(50ms)
  • release(turn_on)
  • delay(50ms)
  • press(3)
  • delay(50ms)
  • release(3)
  • delay(50ms)
  • etc.

I am using 'bluebird' and the communication towards the AVR is already written. All I am struggling with is how I can write the above set of commands with promises.

I came to the following code, which works, but I don't know if it is the most elegant and best solution.

var arr = [1,2,3,4,5];
Promise.mapSeries(arr, function (itm) {
      console.log('on: ' + itm);
      return Promise.delay(500).then(function() { console.log('off: ' +itm); 
   }).delay(500);
});
\$\endgroup\$
1
  • \$\begingroup\$ Looks fine, however you'll want to use delay(50) instead of 500 \$\endgroup\$
    – Bergi
    Oct 8, 2017 at 16:38

2 Answers 2

2
\$\begingroup\$

The promise is to do the JOB

and report back when done. Promises are not for timeouts to communicate, they do that already via the call back, and a timeout's callback is as good as a promise, with a lot less overhead.

I have not looked at the code for BlueBird so I can not determine its quality and thus your code. But I see two promises where there should only be one, so there is room for improvement.

Why do you need a promise?

You have to consider why you want to have a promise. Creating a promise just so you can start another is adding an extra layer of unneeded complexity.

You want a promise so that you can have some code execute when the job is complete, or if there is a chance the job may not complete, you have a promise to have code executed for that situation.

Whenever you write any sort of code, always ask yourself, is this step really needed, if you can do without then it should not be there.

One promise

The following will do what you want with only one promise needing to be created. The timeouts handle the timing, and all you want to know is that you get a promise to know when the job is done.

const log = arg => console.log(arg);
const keyOn  = key => log("On : "  + key);
const keyOff = key => log("Off : " + key);

const keyClick = (key, ms, end) => {
    keyOn(key);
    setTimeout(() => {
        keyOff(key);
        setTimeout(end, ms);
    }, ms);
}
// using async function means that if the keys array is empty
// you can just let the function fall through to resolve
async function keySequence(keys, ms = 50){
    var complete;  // to hold the resolving promise callback
    const step = () => { 
        keyClick(  // do a key click, keyDown 50ms, keyUp, 50ms
            keys.shift(), // get the next key
            ms,           // delay between keys
            (keys.length > 0 ? step : complete) // call next step or resolve
        ); 
    };
    if (keys.length) {  // only if there are keys
        step();  // start the first key
        return new Promise(done => complete = done ); // get the resolve from promise
    }
    // function fall through automaticly resolves the async promise.

}
keySequence(["1","2","3","4","5"]).then(() => log("done"));

The example can be simplified by having the keyClick body in the step function, I seperated them to make it clearer.

Personally the rampant bad use of promises sometimes makes me wish they were never added to the language. We have gone from callback hell, to the promise chain highway to hell, and by the look of the other answer, awaiting a million promises in hell.

\$\endgroup\$
0
\$\begingroup\$

If you use NodeJS v7.6 or above you can always use async/await to write code that looks synchronous but is in fact asynchronous:

function delay (ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms)
  })
}

async function performActions (actions, ms) {
  for (let action of actions) {
    await press(action)
    await delay(ms)
    await release(action)
    await delay(ms)
  }
}

performActions(["turn_on", 3, 2, 1], 50).then(() => {
  // Completed
}).catch((e) => {
  // An error occured
})

The above code is compatible with the built-in Promises as well as Bluebird's Promises.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.