{"id":26709400,"url":"https://github.com/js-bits/process","last_synced_at":"2025-04-13T17:32:47.162Z","repository":{"id":60581305,"uuid":"544059353","full_name":"js-bits/process","owner":"js-bits","description":"Asynchronous multi-step processing","archived":false,"fork":false,"pushed_at":"2023-07-19T23:22:38.000Z","size":849,"stargazers_count":3,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-27T08:16:43.872Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/js-bits.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}},"created_at":"2022-10-01T14:35:22.000Z","updated_at":"2024-10-03T12:54:13.000Z","dependencies_parsed_at":"2023-02-12T12:16:21.248Z","dependency_job_id":null,"html_url":"https://github.com/js-bits/process","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/js-bits%2Fprocess","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/js-bits%2Fprocess/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/js-bits%2Fprocess/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/js-bits%2Fprocess/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/js-bits","download_url":"https://codeload.github.com/js-bits/process/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248752367,"owners_count":21156078,"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":[],"created_at":"2025-03-27T08:16:47.174Z","updated_at":"2025-04-13T17:32:47.141Z","avatar_url":"https://github.com/js-bits.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Asynchronous multi-step processing\n\nAllows you to organize a complicated processing logic as a set of simpler consecutive steps. Includes [Executor](https://www.npmjs.com/package/@js-bits/executor) capabilities in case you need some metrics.\n\n## Installation\n\nInstall with npm:\n\n```\nnpm install @js-bits/process\n```\n\nInstall with yarn:\n\n```\nyarn add @js-bits/process\n```\n\nImport where you need it:\n\n```javascript\nimport Process from '@js-bits/process';\n```\n\nor require for CommonJS:\n\n```javascript\nconst Process = require('@js-bits/process');\n```\n\n## How to use\n\nLet's say you have a long complicated process that consists of some operations (synchronous or asynchronous) which must be performed sequentially, one by one. `Process` class can help you to simplify the implementation and make it more readable. Here is an example:\n\n```javascript\n(async () =\u003e {\n  // a synchronous function\n  const step1 = () =\u003e {\n    console.log('step1');\n  };\n  // an asynchronous function\n  const step2 = async () =\u003e {\n    console.log('step2');\n  };\n  // a promise\n  const step3 = new Promise(resolve =\u003e {\n    setTimeout(() =\u003e {\n      console.log('step3');\n      resolve();\n    }, 100);\n  });\n  // another process\n  const step4 = new Process(() =\u003e {\n    console.log('step4');\n  });\n  const process = new Process(step1, step2, step3, step4);\n  await process.start();\n  // step1\n  // step2\n  // step3\n  // step4\n})();\n```\n\nAlternatively you can combine steps into groups like this:\n\n```javascript\n...\nconst operation1 = [step1, step2];\nconst operation2 = [step3, step4];\nconst process = new Process(operation1, operation2);\n...\n```\n\nThe result will be the same as before, but the code looks more structured and logically organized.\n\n## Passing input parameters and returning processing results\n\nYou can pass as many input parameters as you need (as an object) into `.start()` method of a `Process` instance.\nSteps of the process may return some results (also as an object) if necessary, but not required to do so.\nEach step of the process will receive (as an argument) all input parameters as well as results of all preceding steps,\nwhich means that the step logic can be implemented to behave differently depending on what happened before.\nThe return value of the whole process will be all step results combined.\n\n```javascript\n(async () =\u003e {\n  const step1 = async args =\u003e {\n    console.log(args); // { inputParam: 1 }\n    return { step1Result: 'success' };\n  };\n  const step2 = async args =\u003e {\n    console.log(args); // { inputParam: 1, step1Result: 'success' }\n    return { step2Result: 'success' };\n  };\n  const step3 = async args =\u003e {\n    console.log(args); // { inputParam: 1, step1Result: 'success', step2Result: 'success' }\n    return { step3Result: 'success' };\n  };\n  const process = new Process(step1, step2, step3);\n  const result = await process.start({ inputParam: 1 });\n  console.log(result); // { step1Result: 'success', step2Result: 'success', step3Result: 'success' }\n})();\n```\n\n## Process.steps() shortcut\n\nThere is `Process.steps()` method available for your convenience.\nIt's just a shortcut to `new Process(...steps).start(input)`.\n\n```javascript\n...\nconst result = Process.steps(step1, step2, step3)(inputParams)\n```\n\nYou can also use it for better structuring of your source-code.\n\n```javascript\n...\nconst operation1 = Process.steps(\n  step1,\n  step2,\n  step3,\n);\nconst operation2 = Process.steps(\n  step4,\n  step5,\n  step6,\n);\nconst process = new Process(operation1, operation2);\n...\n```\n\n## Exit strategy (Process.exit)\n\nMost likely, there will be some exceptional situations when you have to interrupt your processing.\nYou can account for that using `Process.exit` method.\n\n```javascript\nconst step1 = async () =\u003e {\n  console.log('step1');\n  return { step1Result: 'success' };\n};\nconst step2 = async () =\u003e {\n  console.log('step2');\n  return Process.exit;\n};\nconst step3 = async () =\u003e {\n  // this step won't be performed\n  console.log('step3');\n  return { step3Result: 'success' };\n};\nconst process = new Process(step1, step2, step3);\nconst result = await process.start({ inputParam: 1 });\nconsole.log(result);\n// step1\n// step2\n// { step1Result: 'success', [Symbol(EXIT)]: true }\n```\n\nIf you'd like to also return some additional information related to the process interruption,\njust use `Process.exit` as a function (only accepts objects as an argument).\n\n```javascript\n...\nreturn Process.exit({ exitReason: 'Something bad has happened'});\n...\n// { step1Result: 'success', [Symbol(EXIT)]: true, exitReason: 'Something bad has happened' }\n```\n\n## Conditional processing with Process.switch()\n\nSometimes business logic requires a processing to go one way or another depending on some condition.\n`Process.switch()` method is aimed to solve the problem. Here is how it works:\n\n```javascript\n(async () =\u003e {\n  const simulateLatency =\n    func =\u003e\n    (...args) =\u003e\n      new Promise(resolve =\u003e {\n        setTimeout(() =\u003e {\n          resolve(func(...args));\n        }, Math.ceil(Math.random() * 500));\n      });\n\n  const fetchItem = simulateLatency(() =\u003e {\n    console.log('item loaded');\n    const item = { id: 'item-id', state: 'draft' };\n    return { item };\n  });\n  const checkNeedsUpdate = ({ newState, item }) =\u003e {\n    if (item.state === newState) return Process.exit;\n    console.log('item needs update');\n  };\n  const publishItem = simulateLatency(() =\u003e {\n    console.log('item published');\n  });\n  const notifySubscribers = simulateLatency(() =\u003e {\n    console.log('subscribers notified');\n  });\n  const deleteItem = simulateLatency(() =\u003e {\n    console.log('item deleted');\n  });\n  const updateItem = simulateLatency(({ item, newState }) =\u003e {\n    console.log('item updated');\n    return { updateResult: { ...item, state: newState } };\n  });\n\n  const processItem = new Process(\n    fetchItem,\n    checkNeedsUpdate,\n    Process.switch('newState', {\n      // select next step based on input.newState value\n      draft: Process.noop,\n      published: [publishItem, notifySubscribers],\n      deleted: [deleteItem, Process.exit],\n    }),\n    updateItem\n  );\n\n  const input = { id: 'item-id', newState: 'published' };\n  const result = await processItem.start(input);\n\n  console.log(result.updateResult);\n  // item loaded\n  // item needs update\n  // item published\n  // subscribers notified\n  // item updated\n  // { id: 'item-id', state: 'published' }\n})();\n```\n\n`Process.noop` (abbreviation for \"no operation\") above is just a convenient shortcut to `Promise.resolve()`, which is an equivalent of \"do nothing\".\n\n## Notes\n\n- A process is a one-off operation. You have to create a new instance each time you need to run the same process.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjs-bits%2Fprocess","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjs-bits%2Fprocess","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjs-bits%2Fprocess/lists"}