{"id":19747453,"url":"https://github.com/fierycod/node.jsstreams-vs-clojurechannels","last_synced_at":"2025-06-21T17:03:48.999Z","repository":{"id":128553053,"uuid":"143856981","full_name":"FieryCod/Node.jsStreams-vs-ClojureChannels","owner":"FieryCod","description":"Quick comparison between the Clojure/Golang channels and Node.js streams. ","archived":false,"fork":false,"pushed_at":"2018-09-25T22:52:07.000Z","size":2529,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-21T17:03:05.203Z","etag":null,"topics":["clojure","clojurescript","nodejs"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FieryCod.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-07T10:21:40.000Z","updated_at":"2021-02-14T21:14:56.000Z","dependencies_parsed_at":null,"dependency_job_id":"2b976f83-bcb5-4c9f-9851-3174a583830d","html_url":"https://github.com/FieryCod/Node.jsStreams-vs-ClojureChannels","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/FieryCod/Node.jsStreams-vs-ClojureChannels","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FieryCod%2FNode.jsStreams-vs-ClojureChannels","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FieryCod%2FNode.jsStreams-vs-ClojureChannels/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FieryCod%2FNode.jsStreams-vs-ClojureChannels/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FieryCod%2FNode.jsStreams-vs-ClojureChannels/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FieryCod","download_url":"https://codeload.github.com/FieryCod/Node.jsStreams-vs-ClojureChannels/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FieryCod%2FNode.jsStreams-vs-ClojureChannels/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261162060,"owners_count":23118219,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","clojurescript","nodejs"],"created_at":"2024-11-12T02:17:53.533Z","updated_at":"2025-06-21T17:03:43.962Z","avatar_url":"https://github.com/FieryCod.png","language":"JavaScript","readme":"# Node.js Streams vs Golang/Clojure Channels\n\n- [Intro](#org372ec54)\n- [Development \u0026 PR's](#orgbe08ee5)\n- [Task](#org0a43089)\n- [Benchmark](#org22f1157)\n- [Memory usage](#org37133312)\n- [Event loop](#org37133313)\n- [Summary](#org31233122)\n\n\u003ca id=\"org372ec54\"\u003e\u003c/a\u003e\n\n## Intro\nHere you can find the comparision between channels and streams.\nI will focus on provide pros \u0026 cons of both and rationale why do I think certain option wins.\n\n_Images were generated using `.report` files and the [plot.ly](http://plot.ly/)._\n\n_Now the compiled version of a program is available in `/bin` directory. Run `node bin/exec.js` to check it out :) :rocket: :tada: :rocket:_\n\n\n\u003ca id=\"orgbe08ee5\"\u003e\u003c/a\u003e\n\n## Development \u0026 PR's\n\n1.  Install the [Leiningen](https://leiningen.org/) via brew:\n\n    ```\n    brew install leiningen\n    ```\n\n2.  Install [Yarn](https://yarnpkg.com/en/docs/install#mac-stable) via brew or npm:\n\n    ```\n    brew install yarn\n    ```\n\n3.  Install all dependencies:\n\n    ```\n    yarn \u0026\u0026 lein deps\n    ```\n\n4.  Compile Cljs files (You can modify the `settings.cljs` to increase the number of iterations):\n\n    ```\n    lein cljsbuild once prod\n    ```\n\n    It might take a while :rocket:\n5.  Run `yarn prod` to get possible options :tada: :tada: :tada:\n\n\n\u003ca id=\"org0a43089\"\u003e\u003c/a\u003e\n\n## Task\n\nThe task is to take all numbers from the `seeds.file` and process it as follows.\n\n1.  *Split (according to [fizz-buzz](http://wiki.c2.com/?FizzBuzzTest) test) the dataset to four datasets and store the count of each:* (Task #1)\n    -   fizz-numbers \u0026#x2013; A\n    -   buzz-numbers \u0026#x2013; B\n    -   fizzbuzz-numbers \u0026#x2013; C\n    -   other numbers \u0026#x2013; D\n\n2.  *Then these four datasets should be processed with the lowest possible memory allocation:* (Task #2)\n\n\n    | Dataset | Processing formula                               |\n    |---------|--------------------------------------------------|\n    | A       | Group per 3 numbers e.g '(3 6 9) -\u003e A1           |\n    | B       | Group per 5 numbers e.g '(5 10 15 20 25) -\u003e B1   |\n    | C       | Group per 15 numbers e.g '(15 30 45 60 75) -\u003e C1 |\n    | D       | Do not group                                     |\n\n\n3.  *For each A1, B1, C1 process group one by one and for each create a string* `Group x: ${GroupContent}`.\n*The new datasets should consist of those strings:* (Task #3)\n\n\n    | Dataset | Processing formula                                 |\n    |---------|----------------------------------------------------|\n    | A1      | \"Group 1: '(3 6 9)\\n\", \"Group 2: '(9 12 18)\" -\u003e A2 |\n    | B1      | Same as above -\u003e B2                                |\n    | C1      | Same as above -\u003e C2                                |\n    | D1      | As you can see that dataset is not processed -\u003e D1 |\n\n4.  *Create 4 files for dataset and do the following:* (Task #4)\n    -   Inject all strings for each datasets from (Task #3)\n    -   For ending string inject `There was ${CountCounter} numbers processed for stream ${stream_name}`\n    -   Print to the console how long the program took\n\n\n\u003ca id=\"org22f1157\"\u003e\u003c/a\u003e\n\n## Benchmark\n\nI've run each option 10 times for 1 000 000 numbers and here are the results:\n\n| Streams (ms) | Streams with transform | Channels (ms) |\n|--------------|------------------------|---------------|\n| 26605.116539 | 29765.362489           | 28164.848514  |\n| 25203.411413 | 29565.812846           | 27396.837127  |\n| 24620.778972 | 29096.130312           | 27092.127704  |\n| 24807.708090 | 28733.805155           | 27081.614445  |\n| 24309.594895 | 29778.777752           | 26725.460214  |\n| 24692.114949 | 29347.474361           | 26583.327855  |\n| 24727.354629 | 26343.177867           | 26437.005529  |\n| 25258.414988 | 24942.121680           | 26623.881638  |\n| 24900.780390 | 29382.791624           | 26476.086182  |\n| 25318.520212 | 35954.707085           | 26557.018727  |\n\nWhole program took:\n\n\n| Streams (ms)  | Streams with transform | Channels (ms) |\n|---------------|------------------------|---------------|\n| 250451.349866 | 292918.246961          | 269150.369399 |\n\n\nChannels are about 20 sec slower in the benchmark than Streams (about 2-3 seconds slower in each execution compared to streams).\n\n\u003ca id=\"org37133312\"\u003e\u003c/a\u003e\n\n## Memory Usage\n\nMemory usage changes in each execution. Sometimes the Channels allocate 10mb more than Streams sometimes only 4mb more. But in general channels allocate more memory. I think that the main cause of this is that I got the buffered channel for others.\n\n\u003cp align=\"center\"\u003e\n  \u003cspan\u003e Channels \u003c/span\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/FieryCod/Node.jsStreams-vs-ClojureChannels/master/plots/memory_usage_channels.png\" alt=\"Channels memory plot\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cspan\u003e Streams \u003c/span\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/FieryCod/Node.jsStreams-vs-ClojureChannels/master/plots/memory_usage_streams.png\" alt=\"Streams memory plot\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cspan\u003e Streams with transform\u003c/span\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/FieryCod/Node.jsStreams-vs-ClojureChannels/master/plots/memory_usage_streams_with_transform.png\" alt=\"Streams with transform memory plot\"\u003e\n\u003c/p\u003e\n\n\u003ca id=\"org37133313\"\u003e\u003c/a\u003e\n\n## Event loop\n\nChannels always got 2 more requests to event loop then streams. In my opinion the values for both examples are low and satisfactory. These 2 more requests are not relevant in my opinion.\n\n\u003cp align=\"center\"\u003e\n  \u003cspan\u003e Channels \u003c/span\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/FieryCod/Node.jsStreams-vs-ClojureChannels/master/plots/event_loop_channels.png\" alt=\"Channels event loop plot\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cspan\u003e Streams \u003c/span\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/FieryCod/Node.jsStreams-vs-ClojureChannels/master/plots/event_loop_streams.png\" alt=\"Streams event loop plot\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cspan\u003e Streams with transform\u003c/span\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/FieryCod/Node.jsStreams-vs-ClojureChannels/master/plots/event_loop_streams_with_transform.png\" alt=\"Streams with transform event loop plot\"\u003e\n\u003c/p\u003e\n\n\u003ca id=\"org31233122\"\u003e\u003c/a\u003e\n\n## Summary\n\n### Streams \u0026 Streams with transform\n\n| Pros                                    | Cons                                                              |\n|-----------------------------------------|-------------------------------------------------------------------|\n| Fewer memory usage than in Channels     | Couples logic with the source                                     |\n| Fewer Event Loop requests than Channels | Very imperative approach (even stream Transformer won't help)     |\n| Lower execution time than Channels      | Mutations, mutations everywhere. Updates on state are not atomic. |\n|                                         | Hard to test transformation in separation                         |\n\n\n### Channels\n\n| Pros                                                        | Cons                                                   |\n|-------------------------------------------------------------|--------------------------------------------------------|\n| Does not couple the logic at all                            | About 3-10mb more memory usage than streams            |\n| Fewer Event Loop requests than Channels                     | About 2-3 more Event Loop requests                     |\n| Mutation is atomic                                          | Higher execution time about 2-3s per program execution |\n| Concise and easy to understand code                         |                                                        |\n| Easier to cordinate async stuff                             |                                                        |\n| Use of composable transducers                               |                                                        |\n| Quite clean, FP code therefore easier to mantain and extend |                                                        |\n| Easy to test every single transformation                    |                                                        |\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffierycod%2Fnode.jsstreams-vs-clojurechannels","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffierycod%2Fnode.jsstreams-vs-clojurechannels","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffierycod%2Fnode.jsstreams-vs-clojurechannels/lists"}