{"id":19951321,"url":"https://github.com/benpptung/util-asyncflow","last_synced_at":"2025-03-01T14:43:43.027Z","repository":{"id":57388999,"uuid":"89583888","full_name":"benpptung/util-asyncflow","owner":"benpptung","description":"A thin and tiny utility to run async functions in series.","archived":false,"fork":false,"pushed_at":"2017-08-09T06:25:23.000Z","size":47,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-04-15T00:08:08.297Z","etag":null,"topics":["async","async-flow","async-functions","asynchronous-tasks","callbacks","tiny-utility"],"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/benpptung.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":"2017-04-27T10:05:50.000Z","updated_at":"2017-05-03T03:28:20.000Z","dependencies_parsed_at":"2022-09-09T13:22:18.050Z","dependency_job_id":null,"html_url":"https://github.com/benpptung/util-asyncflow","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benpptung%2Futil-asyncflow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benpptung%2Futil-asyncflow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benpptung%2Futil-asyncflow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benpptung%2Futil-asyncflow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benpptung","download_url":"https://codeload.github.com/benpptung/util-asyncflow/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241381520,"owners_count":19953749,"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":["async","async-flow","async-functions","asynchronous-tasks","callbacks","tiny-utility"],"created_at":"2024-11-13T01:07:39.552Z","updated_at":"2025-03-01T14:43:43.007Z","avatar_url":"https://github.com/benpptung.png","language":"JavaScript","readme":"# util-asyncflow\n\nA thin and tiny utility to control async flows. \nThis is similiar to [async](http://caolan.github.io/async/)'s `series`, `seq`, `compose`, `waterfall`... in control flow section. Just 240 lines.\n\nSee also [util-retry](https://www.npmjs.com/package/util-retry) - an async function retry flow utility\n\n#### Why this?\n- This code is simpler, and has `NO` dependency.\n\n- Few methods to learn for async flows control. \n\n- Flexible to manage arguments and results during async flow.\n\n- Easy to connect multiple async flows and less callback hell, see [`example 5`](#example-5)\n\n- Make codes clean and readable using async function and callback.\n\n\n\n# installation\n\n```\nnpm install util-asyncflow\n```\n\n\n# API\n\n### new AsyncFlow([option, [thisArg]]) \n\n#### option:\n\n- prepend: {Boolean}, default to false. Prepend previous async function result to the arguments of next async function like [`example 1`](#example-1)\n\n- output: '`last`|`collection`|`rest`', default to `last`. This option control how all results returned in the `final callback`. \n\n  -`last`: callback last function result\n  \n  -`collection`: Receive all task results in a collection style, so it is easy to handle collection via `forEach`, `map`...   See [`example 4`](#example-4)\n  \n  -`rest`: Receive all task results too, but in a `rest` style.  see [`example 2`](#example-2)\n  \n- halt: {Boolean}, default to false. If halt is true, the whole async flow won't run till .go() is called.\n\n\n#### thisArg:\n\nif thisArg exits, all task functions in the flow will be bound to this `thisArg`.\n\n\u003cb\u003eexample:\u003c/b\u003e send context as second option\n\n```\n\nprototype.method = function(arg, cb) {\n\n  var fl = new AsyncFlow(this);\n  \n  fl.task(this.method2, arg);\n  \n  fl.wait(this.method3)\n  \n  fl.run(cb)\n}\n\n```\nAll `method2`, `method3`, in the above `method` will be bound to `this` automatically.\n\n\n### .task(fn, [arg1,[arg2...)\n\nAdd an async function as a task following necessary arguments. ( shortname: `.t()` )\n\n### .wait(fn, [art1, [art2...) \n\nAdd an async function as a task too, but it will wait for previous task's result as part of arguments. This behavior is similar to `seq`, `compose`, `waterfall` in `async`. ( shortname: `.w()` )\n\n### .send([arg1,[arg2...)\n\nsend arguments to next Async flow or task.\n\n```\nvar fl = new Async();\n\nfl.task(fn1);\n\nfl.task(fn2);\n\nfl.send(arg);\n\nfl.run(fl2.go)\n\nvar fl2 = new Async({halt: true});\n\nfl2.wait(fn3) // fn3 will recieve `arg`\n\n```\n\nBoth of the followings are the same\n\n```\nfl.task(fn, arg1, arg2, arg3);\n\nfl.send(arg1, arg2, arg3).wait(fn)\n```\n\n\n\n### .ctx(thisArg)\n\nupdate thisArg during this async calls flow.\nCall `flow.ctx()`, will set `thisArg` to `null`.\n\n\nSo, we can initiate flow with `this` context\n\n```\nvar flow = new Asyncflow(this);\n\nflow.task(this.method, arg1, arg2...)\n```\n\nswitch to `this.db` if necessary\n```\nflow.ctx(this.db).wait(this.db.method)\n\nflow.run(cb)\n```\n\nShortname is `.c()`\n\n### .run([callback])\n\nStart this async functions flow with an optional `final callback`. This callback will recieve results based on `output` option. \n\n### .go()\n\nTrigger `new AsyncFlow({halt: true})` to go.\n\n`.go` expects an Error as its first argument. When it is called, the halted async flow will start to run, so we can start another async flow without callback hell.\n\n \n\n\n\nSee [`example 5`](#example-5)\n\n### property: results {Array}\n\nAll original results are stored in `async.results` in order.\n\n### property: length\n\nHow many tasks remaining in the current flow. Same to flow.results.length.\n\n```\n\nfl.task(fn);\n\nfl.task(next=\u003e {\n\n  var res = fl.in(fl.length - 1); // retrieve previous task result\n})\n\n```\n\n\n### .in(index)\n\nA shorthand to retrieve `results[index][0]`, so we can write something like following:\n\n```\nvar flow = new AsyncFlow();\n\nflow.task(UserModel.findById, 1);  // #1 task\n\nflow.wait(TaskModel); // #2 task\n\nflow.task(next=\u003e {\n  let user = flow.in(0); // get user found in #1 task\n  \n  service.sendNotification(user, 'Task Created', next);\n})\n...\n\n```\n\ninstead of \n```\nflow.task(next=\u003e {\n  let user = flow.results[0][0]; // get user found in #1 task\n  ...\n}\n\n```\n\nIf this async flow was triggered by `.go()`, `.in(0)` will return the argument sent by previous async flow.\n\n```\nfl.in(-1)\n\nfl.in(fl.length - 1)\n```\nBoth of the above are same. \n\n\n# Example 1\n\nprepend previous function result to next function arguments\n\n```\n'use strict'\n\n/**\n * Modules\n */\nconst Async = require('util-asyncflow');\nconst fs = require('fs');\nconst join = require('path').join;\nconst dirname = require('path').dirname;\nconst mkdirp = require('mkdirp');\nconst inspect = require('util').inspect;\nconst color = require('colors');\n\n/**\n * instances\n */\nvar async = new Async({prepend: true});\nvar dest = join(__dirname, 'xb', 'dc', 'hello.txt');\nvar src = join(__dirname, 'hello.txt');\n\n// make destination directory\nasync.task(mkdirp, dirname(dest));\n\n// create source file\nasync.task(function(next) {\n\n  fs.createWriteStream(src)\n    .on('close', _=\u003e {\n      console.log(`\\n  ${src}`.green + ' was created.'.cyan );\n      next(null, src)\n    })\n    .on('error', next)\n    .end('hello world');\n\n});\n\n// move source file to destination\nasync.wait(fs.rename, dest); // prepend previous result as first argument\n\n// exec\nasync.run( err=\u003e {\n\n  if (err) return console.error(inspect(err));\n  console.log('  hello.txt'.green + ' was moved to '.cyan + dest.green + '\\n');\n});\n```\n\n# Example 2\n\nreturn all results\n\n```\n// similar to add2() in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function\n\nconst Async = require('util-asyncflow');\n\nfunction resAfter2Sec(x, cb) {\n  setTimeout(_=\u003e cb(null, x), 2000);\n}\n\nfunction add2(x, cb) {\n\n  Async({output: 'rest'}).task(resAfter2Sec, 20).task(resAfter2Sec, 30)\n    .run( (err, a, b)=\u003e cb(null, x + a + b) );\n}\n\nconsole.time('add2');\nadd2(10, (err, res)=\u003e {\n  console.log(res);\n  console.timeEnd('add2');\n});\n```\n\n\n# Example 3\n\n\nHandle collection and dynamically add new async function\n\n```\n'use strict';\n\n/**\n * modules\n */\nconst Async = require('util-asyncflow');\nconst join = require('path').join;\nconst fs = require('fs');\nconst inspect = require('util').inspect;\nconst colors = require('colors');\n\n/**\n * instances\n */\n\nvar dirs = ['dir1', 'dir2', 'dir3'];\nvar async = new Async({output: 'collection'});\n\n// forEach dirs\ndirs.map(dir=\u003e join(__dirname, dir))\n  .forEach(dir=\u003e {\n    async.task(fs.readdir, dir)\n  });\n\n// write `hello` into new files\nasync.run(function(err, results) {\n\n  // manually concat the results\n  var files = results.reduce((pre, cur)=\u003e pre.concat(cur), []);\n\n  // another forEach usage case\n  files.forEach(file=\u003e {\n    async.task(fs.writeFile, join(__dirname, file), 'hello')\n  });\n\n  async.run();\n});\n```\n\n\n# Example 4\n\nUse simple `forEach` to map value in collection through an async function in series.\n\n```\n'use strict';\n\n/**\n * Modules\n */\nconst Async = require('util-asyncflow');\nconst join = require('path').join;\nconst fs = require('fs');\nconst inspect = require('util').inspect;\nconst colors = require('colors');\n\n/**\n * instances\n */\n\nvar nums = [1, 2, 3, 4, 5];\nvar async = new Async({output: 'collection'});\n\nfunction square(x, done) {\n  done(null, Math.pow(x, 2));\n}\n\nnums.forEach(num=\u003e async.task(square, num));\n\nasync.run((err, results)=\u003e {\n  console.log(results);\n});\n```\n\n# Example 5\n\nFrom time to time, we have to stop and catch something to decide if go further or not. In that case, to avoid callback hell, we can connect multiple async flows to avoid callback hell using option `halt` and `.go()`.\n\nCall .go() directly\n```\n\n///// flow 1\nvar fl1 = new Async();\nfl1.task(mul3async, 2);\nfl1.wait(mul3async);\nfl1.run((err, res)=\u003e {\n\n  if (err) return console.error(err);\n\n  console.log(res);\n  console.log('==== end of fl1');\n\n\n  if (res \u003e 18) return;\n  fl2.go(null, res);\n});\n\n\n///// flow 2\nvar fl2 = new Async({halt: true});\nfl2.wait(mul3async);\nfl2.wait(mul3async);\nfl2.run((err, res)=\u003e {\n  if (err) return console.error(err);\n\n  console.log();\n  console.log(res);\n  console.log('===== end of fl2');\n\n});\n\n\n\n/////////////////////\nfunction pow2inputAfterLong(x, next) {\n  setTimeout(_=\u003e {\n    next(null, Math.pow(x, 2));\n  }, 2000)\n}\n\nfunction mul3async(x, next) {\n  setTimeout(_=\u003e {\n    next(null, x * 3);\n  }, 1000)\n}\n```\n\n\n\nPut .go as a callback\n\n```\n\n///// flow 1\nvar fl1 = new Async();\nfl1.task(mul3async, 2);\nfl1.wait(mul3async);\nfl1.run((err, res)=\u003e {\n\n  if (err) return console.error(err);\n\n  console.log(res);\n  console.log('==== end of fl1');\n\n\n  mul3async(2, fl2.go)\n});\n\n\n///// flow 2\nvar fl2 = new Async({halt: true});\nfl2.wait(mul3async);\nfl2.wait(mul3async);\nfl2.run((err, res)=\u003e {\n  if (err) return console.error(err);\n\n  console.log();\n  console.log(res);\n  console.log('===== end of fl2');\n\n});\n\n\n\n/////////////////////\nfunction pow2inputAfterLong(x, next) {\n  setTimeout(_=\u003e {\n    next(null, Math.pow(x, 2));\n  }, 2000)\n}\n\nfunction mul3async(x, next) {\n  setTimeout(_=\u003e {\n    next(null, x * 3);\n  }, 1000)\n}\n```\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenpptung%2Futil-asyncflow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenpptung%2Futil-asyncflow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenpptung%2Futil-asyncflow/lists"}