{"id":14956411,"url":"https://github.com/maksimka101/combine","last_synced_at":"2025-06-23T08:37:45.834Z","repository":{"id":39967750,"uuid":"482357191","full_name":"Maksimka101/combine","owner":"Maksimka101","description":"A Flutter package which allows you to work with MethodChannels in Isolate and provides simplified Isolate and Thread Pool API","archived":false,"fork":false,"pushed_at":"2024-07-04T08:36:46.000Z","size":498,"stargazers_count":28,"open_issues_count":4,"forks_count":6,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-29T09:38:11.744Z","etag":null,"topics":["binary-messenger","dart","flutter","isolate","method-channel","platform-channel","thread-pool","worker-manager"],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Maksimka101.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2022-04-16T20:43:47.000Z","updated_at":"2025-01-26T23:25:58.000Z","dependencies_parsed_at":"2023-12-28T22:25:53.513Z","dependency_job_id":"36471cde-9a29-497d-b10d-1271dd66a57d","html_url":"https://github.com/Maksimka101/combine","commit_stats":{"total_commits":56,"total_committers":1,"mean_commits":56.0,"dds":0.0,"last_synced_commit":"0d4e335c8fc9bacc40d1fcf1ae0fbb7b08fab764"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maksimka101%2Fcombine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maksimka101%2Fcombine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maksimka101%2Fcombine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maksimka101%2Fcombine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Maksimka101","download_url":"https://codeload.github.com/Maksimka101/combine/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237944113,"owners_count":19391588,"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":["binary-messenger","dart","flutter","isolate","method-channel","platform-channel","thread-pool","worker-manager"],"created_at":"2024-09-24T13:13:00.908Z","updated_at":"2025-02-09T11:30:54.286Z","avatar_url":"https://github.com/Maksimka101.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Combine logo](assets/combine_logo.png)\n\n\u003ca href=\"https://opensource.org/licenses/MIT\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-MIT-purple.svg\" alt=\"License: MIT\"\u003e\u003c/a\u003e\n\u003ca href=\"https://pub.dev/packages/combine\"\u003e\u003cimg src=\"https://img.shields.io/pub/v/combine.svg\" alt=\"Pub\"\u003e\u003c/a\u003e\n\u003ca href=\"https://codecov.io/gh/Maksimka101/combine\"\u003e\n  \u003cimg src=\"https://codecov.io/gh/Maksimka101/combine/branch/master/graph/badge.svg?token=B6UDB81K4Z\"/\u003e\n\u003c/a\u003e\n\n\nThis plugin **Combines** Isolate, MethodChannel and Thread Pool. \\\nIn other words it provides a way to use flutter plugins in `Isolate`\nor just work with user friendly API for Isolates.\n\nLearn more in [this](https://maksimka101.github.io/docusaurus-blog/blog/combine/) article!\n\n\u003cdetails\u003e\n  \u003csummary\u003eHow it works with Method Channels. Briefly\u003c/summary\u003e\n  \n  For the `MethodChannel` combine is using\n  [`BackgroundIsolateBinaryMessenger`](https://medium.com/flutter/introducing-background-isolate-channels-7a299609cad8).\n\n  In the case of the `BinaryMessenger` combine sending its messages to the UI Isolate \n  and then sends them to the platform. As for now, `BackgroundIsolateBinaryMessenger`\n  can't be used for the `BinaryMessenger`.\n  \n  What's the difference between `MethodChannel` and `BinaryMessenger`?\\\n  `BinaryMessenger` is a Low-Level API for sending messages to the platform. \n  For example, it's used by the `AssetBundle` or event by the `MethodChannel` in the main Isolate.\n\u003c/details\u003e\n\n# Features\n\n- Create an Isolate.\n- Communicate with Isolate without extra code.\n- Use Method Channels in Isolate. \n- Efficiently execute tasks in Isolates pool. \n\n# Index\n\n- [Short usage example](#short-usage-example)\n- [Combine](#combine)\n  - [Create and maintain Isolate](#create-and-maintain-isolate)\n    - [Create](#create)\n    - [Kill](#kill)\n  - [Communicate with Isolate](#communicate-with-isolate)\n    - [IsolateContext](#isolatecontext)\n    - [Pass arguments](#pass-arguments)\n    - [Chat with Isolate](#chat-with-isolate)\n  - [Dealing with MethodChannels](#dealing-with-methodchannels)\n    - [Configuration](#configuration)\n- [Combine Worker](#combine-worker)\n  - [Initialize worker](#initialize-worker)\n  - [Execute tasks](#execute-tasks)\n  - [Create a new instance](#create-a-new-instance)\n    - [Initialize worker isolates](#initialize-worker-isolates)\n  - [Close Worker](#close-worker)\n- [Limitations](#limitations)\n  - [Method Channel](#method-channel)\n  - [Binary Messenger](#binary-messenger)\n  - [Closure variables](#closure-variables)\n\n# Usage\n\n## Short usage example\n\n`Combine` is used to create Isolates. \n\n```dart\nCombineInfo isolateInfo = await Combine().spawn((context) {\n  print(\"Argument from main isolate: ${context.argument}\");\n\n  context.messenger.messages.listen((message) {\n    print(\"Message from main isolate: $message\");\n    context.messenger.send(\"Hello from isolate!\");\n  });\n}, argument: 42);\n\nisolateInfo.messenger\n  ..messages.listen((message) {\n    print(\"Message from isolate: $message\");\n  })\n  ..send(\"Hello from main isolate!\");\n\n// Will print:\n// Argument from main isolate: 42\n// Message from main isolate: Hello from main isolate!\n// Message from isolate: Hello from isolate!\n```\n\n`CombineWorker` is a pool of Isolates that efficiently executes tasks in them.\n\nIn comparison to Fluter's `compute` method which creates an isolate each time\nit is called, Combine Worker creates a pool of isolates and efficiently\nreuses them.\n```dart\nfinal fibonacciNumber = await CombineWorker().executeWithArg(calculateFibonacci, 42);\nprint(fibonacciNumber); // 1655801441\n\nint calculateFibonacci(int number) {\n  if (number == 1 || number == 2) {\n    return 1;\n  } else {\n    return calculateFibonacci(number - 1) + calculateFibonacci(number - 2);\n  }\n}\n```\n\n## Combine\n### Create and maintain Isolate\n\n#### Create\n\n`CombineIsolate` is just a representation of `Isolate` so when you create a CombineIsolate,\nan Isolate will be created under the hood. On the web, however, everything will be executed \non the main isolate.\n\nTo create a new CombineIsolate you just need to call `Combine().spawn(entryPointFunction)`.\n`entryPointFunction` is a function which will be called in Isolate.\n\n`CombineInfo` will be returned which holds `CombineIsolate` to control `Isolate`\nand `IsolateMessenger` to communicate with it.\n\n```dart\nCombineInfo combineInfo = await Combine().spawn((context) {\n  print(\"Hello from Isolate!!!\");\n});\n```\n\n#### Kill\n\nYou can use `CombineIsolate.kill` method to kill CombineIsolate.\n\n```dart\nCombineIsolate combineIsolate;\ncombineIsolate.kill(); // Kill Isolate.\n```\n\n### Communicate with Isolate\n\n#### IsolateContext\n\nDo you remember `context` argument in `entryPointFunction`? Let's take a closer look at it.\n\n`IsolateContext` holds an argument, passed while you spawn Isolate, `IsolateMessenger` \nwhich is used to communicate with original Isolate and `CombineIsolate` which is \nrepresents current Isolate.\n\n#### Pass arguments\n\nYou can just use variables from closure or\nprovide argument by passing it to the `spawn` function.\n\n```dart\nfinal argumentFromClosure = \"This is argument from main Isolate\";\nCombine().spawn(\n  (context) {\n    final argument = context.argument as String;\n    print(argument); // Print: This is my argument\n    print(argumentFromClosure); // Print: This is argument from main Isolate\n  },\n  argument: \"This is my argument\",\n);\n```\n\nArguments from closure will be copied to the Isolate. They may be mutable however mutable\nvariable won't be synchronized so if you change it in main Isolate it won't be changed in \nCombine Isolate.\n\n#### Chat with Isolate\n\nTo chat with Isolate you can use `IsolateMessenger`. \nIt has `messages` getter with stream of messages from Isolate \nand `send` method which sends message to Isolate.\n\nIn the created isolate you can get IsolateMessenger from `IsolateContext.messenger`. \nAnother IsolateMessenger can be found in `CombineIsolate`.\n\n```dart\nCombineInfo combineInfo = await Combine().spawn((context) {\n  context.messenger\n    ..messages.listen(\n      (event) =\u003e print(\"Message from Main Isolate: $event\"),\n    )\n    ..send(\"Hello from Combine Isolate!\");\n});\n\ncombineInfo.messenger.messages.listen(\n  (event) {\n    print(\"Message from Combine Isolate: $event\");\n    combineInfo.messenger.send(\"Hello from Main Isolate!\");\n  },\n);\n```\n\nThis code will give the following output:\n\u003e Message from Combine Isolate: Hello from Combine Isolate! \\\n\u003e Message from Main Isolate: Hello from Main Isolate!\n\n### Dealing with MethodChannels\n\n#### Configuration\n\nEverything is already configured to work with MethodChannels or \nBinaryMessengers so you can just use them!\n\n```dart\nCombine().spawn((context) async {\n  final textFromTestAsset = await rootBundle.loadString(\"assets/test.txt\");\n  print(\"Text from test asset: $textFromTestAsset\");\n  // Print: Text from test asset: Asset is loaded!\n});\n```\n\nExplanation:\n - the point it that `rootBundle` uses BinaryMessenger (low level MethodChannel)\n - let's assume that the file `assets/test.txt` exists and contains `Asset is loaded!` text\n\n\n## Combine Worker\n### Initialize worker\n\nTo initialize worker you may call `CombineWorker().initialize()` however \nit can be lazily initialized on the first execution so you omit calling this method.\n\nAlso this method has `isolatesCount`, `tasksPerIsolate` and `initializer` parameters.\nThe second parameter is used to set maximum number of tasks that one isolate \ncan perform asynchronously. About the `initializer` parameter you can read \n[below](#initialize-worker-isolates).\n\n### Execute tasks\n\nYou can execute task with from zero to five positional arguments using `execute`, \n`executeWithArg`, `executeWith2Args`, `executeWith3Args`, `executeWith4Args` \nand `executeWith5Args` methods.\n\n```dart\nfinal helloWorld = await CombineWorker().execute(zeroArgsFunction);\nfinal maksim = await CombineWorker().executeWithArg(oneArgFunction, \"Maksim\");\nfinal helloArshak  = await CombineWorker().executeWith2Args(\n  twoArgsFunction, \n  \"Hello\", \"Arshak!\"\n);\n\nString zeroArgsFunction() =\u003e \"Hello, World!\";\nString oneArgFunction(String str) =\u003e str;\nString twoArgsFunction(String a, String b) =\u003e \"$a, $b\";\n```\n\nAlso you can execute task with any many count of positional and named arguments \nwith `executeWithApply` method. It works like `Function.apply`. \nIt takes a function to execute and a list of positional and map of named arguments.\n\n```dart\nfinal argumentsDescription = await CombineWorker().executeWithApply(\n  describeArgs, [\"Hello\"], {#namedArg: \"World\"},\n);\n\nString describeArgs(String arg, {required String namedArg}) {\n  return \"Positional argument: $arg. Named argument: $namedArg\";\n}\n```\n\nThis is very powerful method, however I suggest you to use `execute`, \n`executeWithArg`, `executeWith2Args` etc methods.\n\nIf some task will throw an exception, corresponding execute function \nwill completes with this exception.\n\n### Create a new instance\n\nIf you want to have a few workers with different settings for the separate tasks,\nyou can create a new worker instance with the `CombineWorker.newInstance` factory.\n\n#### Initialize worker isolates\n\nSometimes you need to execute some code once worker isolate is created. For example,\nto initialize db connection, configure API client, etc. It can be done with the \n`initializer` function parameter for the `CombineWorker.initialize()` method.\\\n`initializer` is a function that will be executed in each worker isolate during \ntheir creation. \n\n### Close Worker\n\n`CombineWorker().close()` method is used to close the current worker.\\\n`CombineWorker` is a singleton but under the hood it uses a worker manager instance\nwhich can be closed and recreated. It may be useful if you want to cancel \nall running and awaiting tasks (i. e. on user logout).\n\nWhen worker is closed it completes all tasks with `CombineWorkerClosedException`.\\\nIf you want to wait for remaining tasks set `waitForRemainingTasks` parameter to `true`.\nIn that case they won't be completed with exception. \n\nYou can call `execute` or `initialize` methods without awaiting for this future.\nIn that case new worker manager will be created.\n\n## Limitations\n\n### Method Channel\n\nAs `MethodChannel`s are working using \n[`BackgroundIsolateBinaryMessenger`](https://medium.com/flutter/introducing-background-isolate-channels-7a299609cad8)\nthey have their limitations.\n\n### Binary Messenger\n\nEverything will work fine while `BinaryMessenger.send` method is used by you or your plugin.\n\nHowever if `BinaryMessenger.handlePlatformMessage`\nis used by you or your plugin you may notice that these methods are not working.\nThis may happen if you didn't send any data to the platform from this Isolate. \n\nWhy? In short the reason is that plugin just sends all messages from known binary messenger\nin Main Isolate to the Combine Isolate. However binary messenger becomes known \nwhen you send anything to it.\nThe good news is when you want to receive messages from messenger using\n`BinaryMessenger.handlePlatformMessage` method \nalmost always firstly you send some data to it \nso it is very unlikely that you will face this problem.\n\n### Closure variables\n\nIsolate `entryPoint` function for `spawn` method or `task` function for the `execute` methods \nmay be a first-level, as well as a static or top-level.\n\nAlso, it may use closure variables but with some restrictions:\n - closure variable will be copied (as every variable passed to isolate)\n   so it won't be synchronized across Isolates.\n - if you use at least one variable from closure all closure variables\n   will be copied to the Isolate due to this [issue](https://github.com/dart-lang/sdk/issues/36983).\n   It can lead to high memory consumption or event exception because\n   some variables may contain native resources.\n\nDue to the above points, I highly recommend you avoid using closure variables\nuntil this issue is fixed.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaksimka101%2Fcombine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaksimka101%2Fcombine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaksimka101%2Fcombine/lists"}