{"id":21294349,"url":"https://github.com/khc-zhihao/pors","last_synced_at":"2026-02-04T22:33:29.757Z","repository":{"id":57327525,"uuid":"204109947","full_name":"KHC-ZhiHao/Pors","owner":"KHC-ZhiHao","description":"Flow control system.","archived":false,"fork":false,"pushed_at":"2024-11-03T09:52:59.000Z","size":242,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-21T08:57:32.377Z","etag":null,"topics":["flowcontrol","javascript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/KHC-ZhiHao.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":"2019-08-24T04:50:51.000Z","updated_at":"2024-11-03T09:53:03.000Z","dependencies_parsed_at":"2025-01-22T06:44:27.189Z","dependency_job_id":"df18274a-19e9-4063-a43a-d1d8bfc56fd9","html_url":"https://github.com/KHC-ZhiHao/Pors","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/KHC-ZhiHao/Pors","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KHC-ZhiHao%2FPors","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KHC-ZhiHao%2FPors/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KHC-ZhiHao%2FPors/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KHC-ZhiHao%2FPors/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KHC-ZhiHao","download_url":"https://codeload.github.com/KHC-ZhiHao/Pors/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KHC-ZhiHao%2FPors/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29098234,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-04T21:05:08.033Z","status":"ssl_error","status_checked_at":"2026-02-04T21:04:53.031Z","response_time":62,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["flowcontrol","javascript"],"created_at":"2024-11-21T13:58:59.395Z","updated_at":"2026-02-04T22:33:29.736Z","avatar_url":"https://github.com/KHC-ZhiHao.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cbr\u003e\n\u003cp align=\"center\"\u003e\u003cimg src=\"./logo.png\"\u003e\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003ePors\u003c/h1\u003e\n\u003ch3 align=\"center\"\u003eFlow control system\u003c/h3\u003e\n\n\u003ch6 align=\"center\"\u003e\n    \u003ca href=\"https://www.npmjs.com/package/pors\"\u003e\n        \u003cimg src=\"https://img.shields.io/npm/v/pors.svg\"\u003e\n    \u003c/a\u003e\n    \u003ca href='https://github.com/KHC-ZhiHao/pors/actions'\u003e\n        \u003cimg src='https://github.com/KHC-ZhiHao/pors/actions/workflows/build-stage.yml/badge.svg'/\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://coveralls.io/github/KHC-ZhiHao/pors?branch=master\"\u003e\n        \u003cimg src=\"https://coveralls.io/repos/github/KHC-ZhiHao/pors/badge.svg?branch=master\" alt=\"Coverage Status\"  style=\"max-width:100%;\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://standardjs.com/\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/code_style-standard-brightgreen.svg\" alt=\"Standard Code Style\"  style=\"max-width:100%;\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/KHC-ZhiHao/pors\"\u003e\n        \u003cimg src=\"https://img.shields.io/github/stars/KHC-ZhiHao/pors.svg?style=social\"\u003e\n    \u003c/a\u003e\n    \u003cbr\u003e\n\u003c/h6\u003e\n\n\u003cbr\u003e\n\n`pors` 是一個簡單的批次執行排程系統，能夠批量處理函式，這模式有很多人做過了，但都不能夠同時兼任棋子和塞子的角色，所以又花了一點時間做了這個。\n\n---\n\n## 棋子 - pawn\n\n`pawn` 是一個常駐物件，你可以不斷推送執行續給 `pawn`，它會直接執行，若超過同步執行量則會限制同步運行的數量。\n\n### Example\n\n```js\nimport { pawn } from 'pors'\n\nlet count = 0\npawn(2) // 一次允許的執行量，無填入則不會限制\n    .add(done =\u003e {\n        setTimeout(() =\u003e {\n            count += 1\n            done()\n        }, 1000)\n    })\n    .add(done =\u003e {\n        setTimeout(() =\u003e {\n            count += 1\n            done()\n        }, 1000)\n    })\n    .add(done =\u003e {\n        setTimeout(() =\u003e {\n            count += 1\n            done()\n        }, 1000)\n    })\nsetTimeout(() =\u003e {\n    console.log(count) // 2\n}, 1100)\n```\n\n### 優先執行\n\n可以透過 `addFirst` 將該排程插入貯列的最優先：\n\n```js\nimport { pawn } from 'pors'\n\nlet array = [] as number[]\npawn(1)\n    .add((done) =\u003e {\n        setTimeout(() =\u003e {\n            array.push(1)\n            done()\n        }, 25)\n    })\n    .add((done) =\u003e {\n        setTimeout(() =\u003e {\n            array.push(2)\n            done()\n        }, 25)\n    })\n    .addFirst((done) =\u003e {\n        setTimeout(() =\u003e {\n            array.push(3)\n            done()\n        }, 25)\n    })\nsetTimeout(() =\u003e {\n    console.log(array.join()) // 1,3,2\n}, 100)\n```\n\n### 透過 Async Function\n\n可以透過 `addAsync` 省略 `done`、`error` 等 `callback`：\n\n```js\nimport { pawn } from 'pors'\n\nlet array = [] as number[]\npawn(1).addAsync(async() =\u003e {\n    array.push(1)\n})\n```\n\n### 成員\n\n#### size\n\n```js\nlet pawn = pawn(2).add(done =\u003e {\n    setTimeout(() =\u003e {\n        count += 1\n        done()\n    }, 1000)\n})\nconsole.log(pawn.size) // 1\n```\n\n### 事件\n\n#### empty\n\n當系統執行完佇列後觸發\n\n\u003e 系統監聽一樣可以監聽到該事件。\n\n```js\nlet pawn = pawn()\npawn.on('empty', () =\u003e {\n    console.log('done')\n})\n```\n\n##### 建議使用onEmpty\n\n跟 `on` 不同的地方在於 `onEmpty` 宣告的當下若已經沒有執行續，會觸發一次 `callback`。\n\n```js\nlet pawn = pawn()\npawn.onEmpty(() =\u003e {\n    console.log('done')\n})\n```\n\n---\n\n## Event\n\n```js\nimport { pawn } from 'pors'\n\nlet pw = pawn()\npw.on('done', (event) =\u003e {\n    console.log(event)\n    /*\n        {\n            \"name\": \"Pawn\",\n            \"type\": \"done\",\n            \"context\": {\n                \"id\": \"f73ac396-7202-461e-8eb2-e16ef273cf27\",\n                \"thread\": [Function]\n            },\n            \"listener\": {\n                \"id\": \"442a65ce-1972-4ebf-853b-4188dce42c14\",\n                \"off\": [Function]\n            }\n        }\n    */\n})\npw.add(done =\u003e done())\n```\n\n### 取消監聽\n\n`event` 事件呼叫後會回傳一個 `listener` 物件，藉由宣告 `off` 取消監聽：\n\n```js\nlet listener = pawn().on('done', (event) =\u003e {})\nlistener.off()\n```\n\n藉由 `off` 與id接口取消 `event`：\n\n```js\nlet pw = pawn()\nlet listener = pw.on('done', (event) =\u003e {})\npw.off('done', listener.id)\n```\n\n### 系統事件\n\n所有的事件都會觸發系統層監聽的事件：\n\n```js\nimport { on } from 'pors'\n\non('done', () =\u003e {\n    console.log('123')\n})\n\npawn().add(done =\u003e done())\n```\n\n### 通用事件\n\n不論棋子還是塞子都有以下三種事件\n\n#### run\n\n每個執行續執行前會觸發該事件。\n\n#### done\n\n每個執行續執行前完整結束後會觸發該事件。\n\n#### error\n\n每個執行續執行錯誤會觸發該事件。\n\n```js\npawn().add((done, error) =\u003e {\n    error() // 由此觸發\n})\n```\n\n---\n\n## 批量處裡\n\n`each` 可以一次迭代一個陣列：\n\n```js\npawn().each([1,2,3,4], (value, index, done, error) =\u003e {\n    // do something...\n})\n```\n\n也可以直接填入數字：\n\n```js\npawn().each(5, (value, index, done, error) =\u003e {\n    console.log(value === index) // true\n    // do something...\n})\n```\n\n---\n\n## 清空排程\n\n`clear` 可以清空所有正在等待執行的排程：\n\n```js\npawn().add(d =\u003e d()).clear()\n```\n\n---\n\n## 塞子 - stopper\n\n塞子是預先加入執行續，最後再宣告執行：\n\n### Example\n\n```js\nimport { stopper } from 'pors'\n\nlet count = 0\nstopper(2) // 一次允許的執行量，無填入則不會限制\n    .add(done =\u003e {\n        count += 1\n        done()\n    })\n    .add(done =\u003e {\n        count += 1\n        done()\n    })\n    .start(() =\u003e {\n        console.log(count) // 2\n    })\n```\n\n### 錯誤處理\n\n塞子在宣告 `error` 後會直接中斷所有程序，並將 `result` 傳入 `callback`：\n\n```js\nimport { stopper } from 'pors'\n\nlet count = 0\nstopper(2) // 一次允許的執行量，無填入則不會限制\n    .add((done, error) =\u003e {\n        error('123')\n    })\n    .start((error) =\u003e {\n        console.log(error) // '123'\n    })\n```\n\n### 透過 Async Function\n\n可以透過 `addAsync` 省略 done、error 等 callback：\n\n```js\nimport { stopper } from 'pors'\n\nlet array = [] as number[]\nstopper(1).addAsync(async() =\u003e {\n    array.push(1)\n})\n```\n\n### 塞子事件\n\n#### Process\n\n\u003e 系統監聽一樣可以監聽到該事件。\n\n```js\nlet step = stopper()\nstep.on('process', ({ loaded, totalThread }) =\u003e {\n    console.log(`${loaded}/${totalThread}`)\n})\n```\n\n### 關閉程序\n\n執行 `start` 後會得到一個 `process` 物件，宣告 `close` 可以中斷執行續：\n\n```js\nimport { stopper } from 'pors'\n\nlet count = 0\nstopper(2)\n    .add((done, error) =\u003e {\n        error('123')\n    })\n    .start((error) =\u003e {\n        console.log(error)\n    })\n    .close()\n```\n\n---\n\n## 幫浦 - pump\n\n一個累積計數等待回呼的工具：\n\n```js\nimport pors from 'pors'\nlet pump = pors.pump(() =\u003e console.log('OuO'))\npump.add(2)\npump.press()\npump.press() // 'OuO'\n```\n\n[npm-image]: https://img.shields.io/npm/v/pors.svg\n[npm-url]: https://npmjs.org/package/pors\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhc-zhihao%2Fpors","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkhc-zhihao%2Fpors","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhc-zhihao%2Fpors/lists"}