diff --git a/docs/change-log.md b/docs/change-log.md index 7ce47fb2..aef9e13a 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -30,6 +30,7 @@ - Add `m.censor`. ([#2538](https://github.com/MithrilJS/mithril.js/pull/2538) [@isiahmeadows](https://github.com/isiahmeadows)) - Re-add stream bundles. ([#2539](https://github.com/MithrilJS/mithril.js/pull/2539) [@isiahmeadows](https://github.com/isiahmeadows)) - Remove extra isLifecycleMethod call from removeAttr. ([#2594](https://github.com/MithrilJS/mithril.js/pull/2594) [@ZeikJT](https://github.com/zeikjt)) +- Fix issue where ending a stream in the middle of a stream callback would result in erroneous parent stream state for the rest of that emit. ([#2603](https://github.com/MithrilJS/mithril.js/pull/2603) [@isiahmeadows](https://github.com/isiahmeadows)) Important note: if you were using any of these undocumented tools, they are no longer available as of this release. This is not considered a breaking change as they were written for internal usage and as of v2 are all 100% unsupported in userland. diff --git a/stream/stream.js b/stream/stream.js index 384c5a0d..98f41d96 100644 --- a/stream/stream.js +++ b/stream/stream.js @@ -29,7 +29,11 @@ function Stream(value) { if (open(stream)) { stream._changing() stream._state = "active" - dependentStreams.forEach(function(s, i) { s(dependentFns[i](value)) }) + // Cloning the list to ensure it's still iterated in intended + // order + dependentStreams.slice().forEach(function(s, i) { + if (open(s)) s(this[i](value)) + }, dependentFns.slice()) } } diff --git a/stream/tests/test-stream.js b/stream/tests/test-stream.js index 30f3a1f3..fa1a78c6 100644 --- a/stream/tests/test-stream.js +++ b/stream/tests/test-stream.js @@ -494,6 +494,40 @@ o.spec("stream", function() { stream(2) o(stream()).equals(2) }) + // https://github.com/MithrilJS/mithril.js/issues/2601 + o("ended stream doesn't affect emit of subsequent streams", function() { + const refreshing = Stream() + const o1Received = [] + const waitingReceived = [] + const o2Received = [] + const o3Received = [] + const o4Received = [] + + /* eslint-disable array-callback-return */ + refreshing.map(function(v) { o1Received.push(v) }) + + const waiting = refreshing.map(function(v) { + waitingReceived.push(v) + if (v === false) { + waiting.end(true) + } + }) + + refreshing.map(function(v) { o2Received.push(v) }) + refreshing.map(function(v) { o3Received.push(v) }) + refreshing.map(function(v) { o4Received.push(v) }) + /* eslint-enable array-callback-return */ + + refreshing(true) + refreshing(false) + refreshing("more") + + o(o1Received).deepEquals([true, false, "more"]) + o(waitingReceived).deepEquals([true, false]) + o(o2Received).deepEquals([true, false, "more"]) + o(o3Received).deepEquals([true, false, "more"]) + o(o4Received).deepEquals([true, false, "more"]) + }) }) o.spec("toJSON", function() { o("works", function() {