{"id":25000164,"url":"https://github.com/thinkmill/node-worker","last_synced_at":"2026-01-21T05:44:20.967Z","repository":{"id":51841709,"uuid":"122023455","full_name":"Thinkmill/node-worker","owner":"Thinkmill","description":"🎡 A simple, promise-based, worker framework.","archived":false,"fork":false,"pushed_at":"2021-05-09T15:10:21.000Z","size":41,"stargazers_count":3,"open_issues_count":2,"forks_count":0,"subscribers_count":25,"default_branch":"master","last_synced_at":"2025-04-12T00:17:10.148Z","etag":null,"topics":["nodejs","worker"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Thinkmill.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}},"created_at":"2018-02-19T05:40:09.000Z","updated_at":"2019-10-02T06:15:45.000Z","dependencies_parsed_at":"2022-08-30T15:20:47.651Z","dependency_job_id":null,"html_url":"https://github.com/Thinkmill/node-worker","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thinkmill%2Fnode-worker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thinkmill%2Fnode-worker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thinkmill%2Fnode-worker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thinkmill%2Fnode-worker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Thinkmill","download_url":"https://codeload.github.com/Thinkmill/node-worker/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248543825,"owners_count":21121837,"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":["nodejs","worker"],"created_at":"2025-02-04T19:31:42.336Z","updated_at":"2026-01-21T05:44:20.940Z","avatar_url":"https://github.com/Thinkmill.png","language":"JavaScript","readme":"Worker Framework\n================================================================================\n\nA simple, promise-based, worker framework.\n\n\nInstall\n--------------------------------------------------------------------------------\n\n```sh\nyarn add @thinkmill/node-worker\n```\n\nConstructor\n--------------------------------------------------------------------------------\n\n| Argument | Type | Description |\n|----------|------|-------------|\n| `label` | `String` | A label that describes the worker (used in logs, errors, etc) |\n| `payload` | `Function` | A promise-returning function that is executed on schedule (received the run ordinal) |\n| `options` | `Object` | Options controlling how the worker should behave |\n\n### `payload`\n\nThe `payload` function represents the main body of the worker, where the real work is done.\nIt should return a promise that resolves or rejects withing the `timeoutMs` provided to it.\n\nThe `payload` is invoked according to the following schedule:\n\n* 1000 ms after the `worker.start()` function is called\n* If the promise **rejects** or returns a **truthy** value, the worker will sleep for it's configured `sleepMs` period before invoking the `payload`\n* If the promise returns a **falsey** value the worker will sleep for 1000 ms before invoking the `payload`\n\nWhen invoked, the `payload` will be provided with a single argument; an object containing the following:\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `label` | `String` | The worker label that was provided on construction |\n| `ordinal` | `Number` | An integer indicating how many times the payload has been executed |\n| `timeoutMs` | `Number` | The number of milliseconds the worker will wait for this invocation to return |\n\n### `options`\n\nThe `options` can contain:\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `sleepMs` | `Number` | How long do we pause between runs? (in milliseconds) |\n| `timeoutMs` | `Number` | How long do we wait (in milliseconds) for the run promise to resolve/reject? See important notes below! |\n\nNote that the `timeoutMs` provided forces the end of a cycle (and allows the next run to be scheduled) but does not (and cannot?) terminate the still-running promise.\nIf the promise returned by the `payload` function has errored internally (without resolving or rejecting) then that's OK; the schedule timeout will prevent the worker from stalling forever.\n_But_ if the promise returned is still doing work, there's the possibility we'll end up with **multiple instances** of the payload executing in tandem.\nThis is almost certainly a Bad Thing, but that's up to you.\n\nThe take away:\n\n* Make sure your worker promises always resolve\n* Probably set a fairly long `timeoutMs` value\n\n### Instance methods\n\n| Method | Description |\n| ------ | ----------- |\n| `start()` | Start the worker after a short delay. |\n| `stop()` | Stop the worker. The currently running job will be allowed to compete but the worker will not restart afterwords. |\n| `scheduleRunInMs(delayMs, onceOff`) | Schedule a run in `delayMs` milliseconds. If `onceOff` is `true` this will trigger an extra run rather than rescheduling the next run. If the worker has been stopped this will have no effect. |\n\n\nUsage\n--------------------------------------------------------------------------------\n\n### Examples\n\nA simple example:\n\n```js\nconst Worker = require('@thinkmill/node-worker');\n\nconst myWorker = new Worker(\n  'test-worker',\n  ({ label, ordinal, timeoutMs }) =\u003e {\n    return new Promise((resolve, reject) =\u003e {\n      const takeMs = 4000 + (Math.random() * 1500);\n      console.log(`Run #${ordinal} ..\n        will take ${takeMs} ms`);\n      setTimeout(() =\u003e {\n        console.log(`Run #${ordinal} ..\n          resolving`);\n        resolve(true);\n      }, takeMs);\n    });\n  }, {\n    sleepMs: 5000,\n    timeoutMs: 5 * 1000,\n  }\n);\n\nmyWorker.start();\n```\n\nA more realistic/interesting example, processing items in a queue:\n\n```js\nconst Worker = require('@thinkmill/node-worker');\nconst debug = require('debug')('workers:dequeue-things');\nconst Model = require('../models/queuedThings');\n\n// Manage the dequeuing of things\nconst payload = async ({ label, ordinal, timeoutMs }) =\u003e {\n  const runForMs = timeoutMs - 1000;\n  const runUntil = new Date(Date.now() + runForMs);\n\n  debug(`Running for ${runForMs} ms (until ${runUntil.toISOString()})`);\n\n  let processedCount = 0;\n  let queueEmptied = false;\n  let nextThing;\n\n  do {\n    await knex.transaction(async (trx) =\u003e {\n\n      // Get the next thing from the queue\n      nextThing = await Model.query(trx).findOne('isReady', true).whereNull('processedAt').orderBy('queuedAt');\n\n      // The queue is empty; exit early\n      if (!nextThing) {\n        queueEmptied = true;\n        return;\n      }\n\n      // Do whatever it is that things do\n      // ..\n\n      // Record that we've processed this thing\n      await Model.query(trx).update({ processedAt: new Date() }).where({ id: nextThing.id });\n\n      // Inc. our count\n      processedCount++;\n    });\n  }\n  while (new Date() \u003c runUntil);\n\n  // Output some debug info\n  const summaryMsg = `DONE: ${processedCount} things processed, leaving the queue ${queueEmptied ? 'EMPTY' : 'NOT EMPTY'}`;\n  debug(summaryMsg);\n  // Resolve with a boolean indicating whether the payload should be re-invoked soon or after the normal sleep\n  return queueEmptied;\n};\n\n// Create the worker instance and start it\nconst worker = new Worker('dequeue-things', payload, { sleepMs: 60 * 1000 });\nworker.start();\n```\n\nYou can also request a once-off worker run from some other action. For example, if you have a worker sending emails which processes a queue every 10 minutes, you might want to trigger that worker after a successful account creation.\n\n```js\nclass AccountCreator {\n\n\tonCreateSuccess () {\n\t\t// Tell the email worker to run so the user receives email promptly.\n\t\tconst onceOff = true;\n\t\trequire('../email-worker').scheduleRunInMs(200, onceOff);\n\t}\n}\n\n\n```\n\n\n### Debug\n\nWe use the [`debug`](https://www.npmjs.com/package/debug) package internally.\nEntries scoped to `workers:${label}` (where `label` is that supplied on construction).\nIt's probably helpful to follow this pattern in your own `payload` functions.\n\nOutput can be enabled by supplying a scope (or list of scopes) to output in the `DEBUG` env var.\nThis can include wildcards. Eg\n\n```sh\n# All worker debug\nDEBUG=workers:* yarn start\n\n# Debug for a specific worker\nDEBUG=workers:send-notifications yarn start\n\n# Debug for several specific workers\nDEBUG=workers:send-notifications,workers:send-emails yarn start\n```\n\n\nLicense\n--------------------------------------------------------------------------------\n\nBSD Licensed.\nCopyright (c) Thinkmill 2018.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthinkmill%2Fnode-worker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthinkmill%2Fnode-worker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthinkmill%2Fnode-worker/lists"}