{"action":"created","issue":{"url":"https://api.github.com/repos/cujojs/most/issues/213","repository_url":"https://api.github.com/repos/cujojs/most","labels_url":"https://api.github.com/repos/cujojs/most/issues/213/labels{/name}","comments_url":"https://api.github.com/repos/cujojs/most/issues/213/comments","events_url":"https://api.github.com/repos/cujojs/most/issues/213/events","html_url":"https://github.com/cujojs/most/issues/213","id":141860337,"number":213,"title":"Merged Streams Error","user":{"login":"jordalgo","id":1537292,"avatar_url":"https://avatars.githubusercontent.com/u/1537292?v=3","gravatar_id":"","url":"https://api.github.com/users/jordalgo","html_url":"https://github.com/jordalgo","followers_url":"https://api.github.com/users/jordalgo/followers","following_url":"https://api.github.com/users/jordalgo/following{/other_user}","gists_url":"https://api.github.com/users/jordalgo/gists{/gist_id}","starred_url":"https://api.github.com/users/jordalgo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jordalgo/subscriptions","organizations_url":"https://api.github.com/users/jordalgo/orgs","repos_url":"https://api.github.com/users/jordalgo/repos","events_url":"https://api.github.com/users/jordalgo/events{/privacy}","received_events_url":"https://api.github.com/users/jordalgo/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":0,"created_at":"2016-03-18T13:03:17Z","updated_at":"2016-03-19T03:07:24Z","closed_at":null,"body":"@briancavalier - At the moment, a stream created by merging two streams ends if one of the streams throws an error, even if you have a catch for it. E.g.:\r\n```\r\nvar mergedStream = stream1.merge(stream2);\r\nmergedStream\r\n.forEach(fn)\r\n.catch(console.log.bind(console));\r\n```\r\nI kinda feel like you should still be able to observe and get events from this merged stream if you have a catch for the error instead of having to do something like:\r\n```\r\nvar mergedStream = most.create(function(add, end, error) {\r\n  stream1\r\n    .forEach(add)\r\n    .catch(console.log.bind(console));\r\n  stream2\r\n    .forEach(add)\r\n    .catch(console.log.bind(console));\r\n});\r\n```\r\nBut was curious what you thought."},"comment":{"url":"https://api.github.com/repos/cujojs/most/issues/comments/198625392","html_url":"https://github.com/cujojs/most/issues/213#issuecomment-198625392","issue_url":"https://api.github.com/repos/cujojs/most/issues/213","id":198625392,"user":{"login":"briancavalier","id":90518,"avatar_url":"https://avatars.githubusercontent.com/u/90518?v=3","gravatar_id":"","url":"https://api.github.com/users/briancavalier","html_url":"https://github.com/briancavalier","followers_url":"https://api.github.com/users/briancavalier/followers","following_url":"https://api.github.com/users/briancavalier/following{/other_user}","gists_url":"https://api.github.com/users/briancavalier/gists{/gist_id}","starred_url":"https://api.github.com/users/briancavalier/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/briancavalier/subscriptions","organizations_url":"https://api.github.com/users/briancavalier/orgs","repos_url":"https://api.github.com/users/briancavalier/repos","events_url":"https://api.github.com/users/briancavalier/events{/privacy}","received_events_url":"https://api.github.com/users/briancavalier/received_events","type":"User","site_admin":false},"created_at":"2016-03-19T03:07:24Z","updated_at":"2016-03-19T03:07:24Z","body":"Hey @jordalgo.  This is a great question, and there's a good bit wrapped in it.  I'll see if I can unpack it and explain.  Please bear with me as this got a little long, and there may be things here you already know!\r\n\r\nFirst, I feel like I should provide a bit of context on errors and error handling in most.js.  The *meaning* of an error in a most.js event stream is that the event stream has entered a state where it cannot possibly produce any more events for some exceptional reason.  IOW, it has failed utterly and unrecoverably.  \r\n\r\nThis is part of the semantic contract of event streams in most.js.  It's very important that *all* event streams obey this contract.  If some code returns a stream to you, it shouldn't matter how the stream was created/transformed/filtered/merged etc before you get your hands on it.  You should be able to interact with it without having to know *how* it was created.\r\n\r\nTo that end, a merged stream is ... just another stream, and so it adheres to the same error contract as any other stream.  Merge's contract is that when any of the \"input\" streams (those that were merged) errors, the merged stream errors.\r\n\r\nIt would certainly be possible to create some new type of merged stream that has different error characteristics.  For example, a new stream that errors only if all of its \"input\" streams error.  However, you'd need to think carefully about it, since not surfacing those errors could create a debugging headache for someone.\r\n\r\nOk, back to your example.\r\n\r\nThe catch you are using is at the very \"right end\" of the stream.  Once the error reaches that point, it has propagated through the entire stream *and* the promise representing the final state.  Since promises are \"write once\" future values, they are a good fit for representing the one time conclusion of a stream, be it a successful ending or an error.  In this case, once the error reaches the promise, the error is final.  The catch cannot \"reach back\" and prevent the stream from erroring.\r\n\r\nInstead, because most.js is declarative, you'd  arrange declaratively, *up front*, what you want the behavior of a stream graph to be, like a blueprint for a machine.  Then, like pressing a big \"Go\" button, you start the machine by calling reduce/drain/observe/forEach.\r\n\r\nSo, one way you might create a merged stream that allows events to continue when any of it's input streams errors is to use `recoverWith` to arrange to send the errors \"somewhere else\", and simply not propagate it through the stream.  I'll use `console.log` as the \"somewhere else\", but you could just as easily parameterize it.  For example:\r\n\r\n```js\r\nimport { merge, empty } from 'most'\r\n\r\nconst handleError = e => {\r\n  console.log(e)\r\n  return empty()\r\n}\r\n\r\n// merged will continue to propagate events even if\r\n// stream1 or stream2 errors.  It will end if *both* stream1\r\n// and stream2 error\r\nconst merged = merge(\r\n  stream1.recoverWith(handleError),\r\n  stream2.recoverWith(handleError)\r\n)\r\n\r\nmerged.forEach(x => console.log(x))\r\n```\r\n\r\nThat's just one possible behavior, of course.  It could be a starting point for a custom operator, for example.  You'd still need to decide what you want to do with the errors, but this is a nice, declarative start. \r\n\r\nI hope that helps to explain the meaning of errors, why the error handling contract is important, and how you might construct your own custom behavior if you need.  That's certainly one goal of most.js: make it possible for people to build custom operators easily from existing ones.\r\n\r\nPlease feel free to continue to discuss!"}}