{"id":16710739,"url":"https://github.com/overminddl1/task_after","last_synced_at":"2025-10-10T21:40:31.970Z","repository":{"id":57555775,"uuid":"89511766","full_name":"OvermindDL1/task_after","owner":"OvermindDL1","description":"Elixir TaskAfter library","archived":false,"fork":false,"pushed_at":"2023-09-02T22:20:10.000Z","size":17,"stargazers_count":37,"open_issues_count":1,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-27T20:06:07.879Z","etag":null,"topics":["after","elixir","task"],"latest_commit_sha":null,"homepage":null,"language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OvermindDL1.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2017-04-26T18:04:19.000Z","updated_at":"2025-03-03T18:38:35.000Z","dependencies_parsed_at":"2024-10-27T12:10:08.280Z","dependency_job_id":null,"html_url":"https://github.com/OvermindDL1/task_after","commit_stats":{"total_commits":11,"total_committers":1,"mean_commits":11.0,"dds":0.0,"last_synced_commit":"98758974b5fcffa17f33595770e4f40afa18bd37"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/OvermindDL1/task_after","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OvermindDL1%2Ftask_after","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OvermindDL1%2Ftask_after/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OvermindDL1%2Ftask_after/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OvermindDL1%2Ftask_after/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OvermindDL1","download_url":"https://codeload.github.com/OvermindDL1/task_after/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OvermindDL1%2Ftask_after/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279005419,"owners_count":26083883,"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","status":"online","status_checked_at":"2025-10-10T02:00:06.843Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["after","elixir","task"],"created_at":"2024-10-12T20:09:26.926Z","updated_at":"2025-10-10T21:40:31.946Z","avatar_url":"https://github.com/OvermindDL1.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TaskAfter\n\nThis is a library to call a function after a set delay.  Usage is as simple as:  `TaskAfter.task_after(500, fn -\u003e do_something_after_500_ms() end)`\n\n## Installation\n\n- [Available in Hex](https://hex.pm/packages/task_after)\n- [Documentation](https://hexdocs.pm/task_after)\n\nInstall this package by adding `task_after` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:task_after, \"~\u003e 1.0.0\"},\n  ]\nend\n```\n\n### Global installation\n\nTo use this globally without needing to add it to your own supervision tree just add this to your configuration:\n\n```elixir\nconfig :task_after, global_name: TaskAfter\n```\n\nFeel free to replace the global name of `TaskAfter` with anything you want. If the global name is unspecified then all usage of TaskAfter must have the `:name` or `:pid` options specified.\n\n### Local Installation\n\nTo use this locally to your application or to give distinct names so you can have different schedulars then just add the `TaskAfter.Worker.start_link/1` to your supervision tree as normal, such as via:\n\n```elixir\nchildren = [\n  worker(TaskAfter.Worker, [[name: MyCustomName]]),\n]\n```\n\nNote the 2 sets of list elements. You can have a nameless worker by leaving the name option out, such as:\n\n```elixir\nchildren = [\n  worker(TaskAfter.Worker, [[]]),\n]\n```\n\nYou will have to acquire the PID some other way, such as by querying your supervisor.\n\n## Usage\n\nThe main interface point is `TaskAfter.task_after/2` and `TaskAfter.task_after/3` where `TaskAfter.task_after/2` just calls `TaskAfter.task_after/3` with an empty set of options to use the defaults.\n\nThe arguments to `TaskAfter.task_after/3` are, in this order:\n\n1. timeout_after_ms -\u003e integer millisecond timeout\n2. callback -\u003e The 0-arg callback function\n3. opts -\u003e Can be:\n   - `name: name` | `pid: pid` -\u003e Specify a non-global task handler, if unspecified that the application `:global_name` must be specified\n   - `id: id` -\u003e A unique id, if nil or unspecified then it is auto-generated\n   - `call_timeout: timeout` -\u003e Override the timeout on calling to the `TaskAfter.Worker`\n   - `no_return: true` -\u003e Do not return the id or error, just try to register and forget results otherwise\n   - `send_result: pid` -\u003e Sends the result of the task to the specified pid\n   - `send_result: :in_process` -\u003e Runs the task in the `TaskAfter.Worker` process to do internal work, do not use this\n\nYou can also cancel a task via `TaskAfter.cancel_task_after/1` and `TaskAfter.cancel_task_after/2` where `TaskAfter.cancel_task_after/1` just defaults to having an empty opts list.\n\nThe arguments to `TaskAfter.cancel_task_after/2` are, in this order:\n\n1. task_id -\u003e A task id\n2. opts -\u003e Can be:\n   * `name: name` | `pid: pid` -\u003e Specify a non-global task handler, if unspecified that the application `:global_name` must be specified\n   * `call_timeout: timeout` -\u003e Override the timeout on calling to the `TaskAfter.Worker`\n   * `no_return: true` -\u003e Do not return the id or error, just try to register and forget results otherwise\n   * `run_result: pid` -\u003e Sends the result of the task to the specified pid after running it as an async task while returning the Task\n   * `run_result: :in_process` -\u003e Runs the task in the `TaskAfter.Worker` process to do internal work, do not use this, returns the value directly though\n   * `run_result: :async` -\u003e Runs the task as an async task and dismisses the result  while returning the Task\n   * `run_result: nil` -\u003e **Default**: Does not run the task now, just cancels it immediately, returns the callback function\n\nYou can change a task via `TaskAfter.change_task_after/2`.\n\nThe arguments to `TaskAfter.change_task_after/2` are, in this order:\n\n1. task_id -\u003e A task ID\n2. opts -\u003e Can be:\n  * `name: name` | `pid: pid` -\u003e Specify a non-global task handler, if unspecified that the application `:global_name` must be specified\n  * `call_timeout: timeout` -\u003e Override the timeout on calling to the TaskAfter.Worker`\n  * `no_return: true` -\u003e Do not return the id or error, just try to register and forget results otherwise\n  * `callback: fun` -\u003e Change the callback to this function\n  * `timeout_after_ms: timeout` -\u003e Change the timeout to this new value\n  * `send_result: pid` -\u003e Sends the result of the task to the specified pid after running it as an async task\n  * `send_result: :in_process` -\u003e Runs the task in the `TaskAfter.Worker` process to do internal work, do not use this\n  * `send_result: :async` -\u003e **Default**: Runs the task as an async task and dismisses the result\n  * `recreate: true` -\u003e If this is passed in then `callback`, `timeout_after_ms`, and `send_result` **must** be specified to be able to recreate the task if it is already elapsed.\n\nNote: Of course if the task has already run then changing a setting on it won't do\nanything unless `recreate: true` is passed in.\n\nNote: When `recreate: true` is used then `callback`, `timeout_after_ms`, and `send_result`\ncan be passed in their value wrapped in a tagged `:default` tuple like\n`timeout_after_ms: {:default, 500}` and it will not change the existing value if not\nrecreating but will use the value if it is.\n\n\nThey can be used as in these examples/tests:\n\n```elixir\n  test \"TaskAfter and forget\" do\n    s = self()\n    assert {:ok, _auto_id} = TaskAfter.task_after(500, fn -\u003e send(s, 42) end)\n    assert_receive(42, 600)\n  end\n\n  test \"TaskAfter and receive\" do\n    assert {:ok, _auto_id} = TaskAfter.task_after(500, fn -\u003e 42 end, send_result: self())\n    assert_receive(42, 600)\n  end\n\n  test \"TaskAfter with custom id\" do\n    assert {:ok, :my_id} = TaskAfter.task_after(500, fn -\u003e 42 end, id: :my_id, send_result: self())\n    assert_receive(42, 600)\n  end\n\n  test \"TaskAfter with custom id duplicate fails\" do\n    assert {:ok, :dup_id} = TaskAfter.task_after(500, fn -\u003e 42 end, id: :dup_id, send_result: self())\n    assert {:error, {:duplicate_id, :dup_id}} = TaskAfter.task_after(500, fn -\u003e 42 end, id: :dup_id, send_result: self())\n    assert_receive(42, 600)\n  end\n\n  test \"TaskAfter lots of tasks\" do\n    assert {:ok, _} = TaskAfter.task_after(400, fn -\u003e 400 end, send_result: self())\n    assert {:ok, _} = TaskAfter.task_after(200, fn -\u003e 200 end, send_result: self())\n    assert {:ok, _} = TaskAfter.task_after(500, fn -\u003e 500 end, send_result: self())\n    assert {:ok, _} = TaskAfter.task_after(100, fn -\u003e 100 end, send_result: self())\n    assert {:ok, _} = TaskAfter.task_after(300, fn -\u003e 300 end, send_result: self())\n    assert {:ok, _} = TaskAfter.task_after(600, fn -\u003e 600 end, send_result: self())\n    assert_receive(100, 150)\n    assert_receive(200, 150)\n    assert_receive(300, 150)\n    assert_receive(400, 150)\n    assert_receive(500, 150)\n    assert_receive(600, 150)\n  end\n\n  test \"TaskAfter non-global by name\" do\n    assert {:ok, pid} = TaskAfter.Worker.start_link(name: :testing_name)\n    {:ok, _auto_id} = TaskAfter.task_after(500, fn -\u003e 42 end, send_result: self(), name: :testing_name)\n    assert_receive(42, 600)\n    GenServer.stop(pid)\n  end\n\n  test \"TaskAfter non-global by pid\" do\n    assert {:ok, pid} = TaskAfter.Worker.start_link()\n    assert {:ok, _auto_id} = TaskAfter.task_after(500, fn -\u003e 42 end, send_result: self(), pid: pid)\n    assert_receive(42, 600)\n    GenServer.stop(pid)\n  end\n\n  test \"TaskAfter in process (unsafe, can freeze the task worker if the task does not return fast)\" do\n    assert {:ok, pid} = TaskAfter.Worker.start_link()\n    s = self()\n    assert {:ok, _auto_id} = TaskAfter.task_after(500, fn -\u003e send(s, self()) end, send_result: :in_process, pid: pid)\n    assert_receive(^pid, 600)\n    GenServer.stop(pid)\n  end\n\n  test \"TaskAfter and cancel timer, do not run the callback\" do\n    cb = fn -\u003e 42 end\n    assert {:ok, auto_id} = TaskAfter.task_after(500, cb)\n    assert {:ok, ^cb} = TaskAfter.cancel_task_after(auto_id)\n  end\n\n  test \"TaskAfter and cancel timer, but its already been run or does not exist\" do\n    assert {:error, {:does_not_exist, :none}} = TaskAfter.cancel_task_after(:none)\n    assert {:ok, auto_id} = TaskAfter.task_after(0, fn -\u003e 42 end, send_result: self())\n    assert_receive(42, 100)\n    assert {:error, {:does_not_exist, ^auto_id}} = TaskAfter.cancel_task_after(auto_id)\n  end\n\n  test \"TaskAfter and cancel but also run the callback in process (unsafe again)\" do\n    assert {:ok, auto_id} = TaskAfter.task_after(500, fn -\u003e 42 end)\n    assert {:ok, 42} = TaskAfter.cancel_task_after(auto_id, run_result: :in_process)\n  end\n\n  test \"TaskAfter and cancel but also run the callback async\" do\n    s = self()\n    assert {:ok, auto_id} = TaskAfter.task_after(500, fn -\u003e send(s, 42) end)\n    assert {:ok, :task} = TaskAfter.cancel_task_after(auto_id, run_result: :async)\n    assert_receive(42, 600)\n  end\n\n  test \"TaskAfter and cancel but also run the callback async while returning result to pid\" do\n    s = self()\n    assert {:ok, auto_id} = TaskAfter.task_after(500, fn -\u003e 42 end)\n    assert {:ok, :task} = TaskAfter.cancel_task_after(auto_id, run_result: s)\n    assert_receive(42, 600)\n  end\n\n  test \"TaskAfter and crash\" do\n    s = self()\n    len = \u0026length/1\n    d = len.([])\n    assert {:ok, _auto_id0} = TaskAfter.task_after(100, fn -\u003e send(s, 21) end)\n    assert {:ok, _auto_id1} = TaskAfter.task_after(250, fn -\u003e send(s, 1/d) end)\n    assert {:ok, _auto_id2} = TaskAfter.task_after(500, fn -\u003e send(s, 42) end)\n    assert_receive(42, 600)\n    assert_receive(21, 1)\n    assert :no_message = (receive do m -\u003e m after 1 -\u003e :no_message end)\n  end\n\n  test \"TaskAfter and replace callback without recreate\" do\n    assert {:ok, auto_id} = TaskAfter.task_after(500, fn -\u003e 1 end, send_result: self())\n    assert {:ok, ^auto_id} = TaskAfter.change_task_after(auto_id, callback: fn -\u003e 2 end)\n    assert_receive(2, 600)\n    assert {:error, {:does_not_exist, ^auto_id}} = TaskAfter.change_task_after(auto_id, callback: fn -\u003e 3 end)\n  end\n\n  test \"TaskAfter and replace callback and timeout with recreate\" do\n    assert {:ok, auto_id} = TaskAfter.task_after(500, fn -\u003e 1 end, send_result: self())\n    assert {:ok, ^auto_id} = TaskAfter.change_task_after(auto_id, recreate_if_necessary: true, timeout_after_ms: 500, send_result: self(), callback: fn -\u003e 2 end)\n    assert_receive(2, 600)\n    assert {:ok, ^auto_id} = TaskAfter.change_task_after(auto_id, recreate_if_necessary: true, timeout_after_ms: 500, send_result: self(), callback: fn -\u003e 3 end)\n    assert_receive(3, 600)\n  end\n\n  test \"TaskAfter and replace callback without timeout with recreate\" do\n    assert {:ok, auto_id} = TaskAfter.task_after(500, fn -\u003e 1 end, send_result: self())\n    assert {:ok, ^auto_id} = TaskAfter.change_task_after(auto_id, recreate_if_necessary: true, timeout_after_ms: 500, send_result: self(), callback: fn -\u003e 2 end)\n    assert_receive(2, 600)\n    assert {:ok, ^auto_id} = TaskAfter.change_task_after(auto_id, recreate_if_necessary: true, timeout_after_ms: {:default, 500}, send_result: self(), callback: fn -\u003e 3 end)\n    assert_receive(3, 600)\n  end\n\n  test \"TaskAfter and replace timeout without recreate\" do\n    assert {:ok, auto_id} = TaskAfter.task_after(200, fn -\u003e 1 end, send_result: self())\n    assert {:ok, ^auto_id} = TaskAfter.change_task_after(auto_id, timeout_after_ms: 500)\n    assert :no_message = (receive do m -\u003e m after 300 -\u003e :no_message end)\n    assert_receive(1, 600)\n  end\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foverminddl1%2Ftask_after","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foverminddl1%2Ftask_after","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foverminddl1%2Ftask_after/lists"}