JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/2.9.33/bluebird.js"></script>
<button id="run">Press this multiple times</button>
<div id="log"></div>
CSS
#log p {
margin: 0 1em;
font-family: monospace;
}
JavaScript
function log() {
var p = $("<p>").appendTo("#log");
$("#log").children().slice(0, -15).remove();
p.text([].map.call(arguments, function(x) {
return typeof x == "string" ? x : JSON.stringify(x);
}).join(" "));
}
function delay(val) {
log("start: ", val);
return new Promise(function(resolve) {
setTimeout(function() {
log("end: ", val);
resolve();
}, 500);
});
}
function extendedChain(p, stream, action) {
// chains a new action to p on every stream event
// until the chain ends before the next event comes
// resolves with the result of the chain and the advanced stream
return Promise.race([
p.then(function(res){return {res:res}}), // wrap in object to distinguish from event
stream // a promise that resolves with a .next promise
]).then(function (arg) {
return arg.next
? extendedChain(p.then(action), arg.next, action) // a stream event happened first
: {res:arg.res, next:stream}; // the chain fulfilled first
});
}
function rec(stream, action, partDone) {
return stream.then(function(arg) {
return extendedChain(action(), arg.next, action).then(function(arg) {
partDone(arg.res);
return rec(arg.next, action, partDone);
});
});
}
var v = 1;
rec(getEvents($("#run"), "click"), function(){
return delay(v++);
}, function(res) {
log("all current done, none waiting");
log("last result", res);
}); // forever
function getEvents(emitter, name) {
var next;
function get() {
return new Promise(function (res) {
next = res;
});
}
emitter.on(name, function() {
next({next: get()});
});
return get();
}