Iterate array with delay in between using promise
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 =&gt; console.log(arg);
const keyOn  = key =&gt; log("On : "  + key);
const keyOff = key =&gt; log("Off : " + key);

const keyClick = (key, ms, end) =&gt; {
    keyOn(key);
    setTimeout(() =&gt; {
        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 = () =&gt; { 
        keyClick(  // do a key click, keyDown 50ms, keyUp, 50ms
            keys.shift(), // get the next key
            ms,           // delay between keys
            (keys.length &gt; 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 =&gt; complete = done ); // get the resolve from promise
    }
    // function fall through automaticly resolves the async promise.

}
keySequence(["1","2","3","4","5"]).then(() =&gt; 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.