{"id":16641224,"url":"https://github.com/cmstead/async-flow-control","last_synced_at":"2025-04-05T01:25:43.421Z","repository":{"id":34644048,"uuid":"181567808","full_name":"cmstead/async-flow-control","owner":"cmstead","description":"Handle Javascript program flow control gracefully in async and mixed async/sync workflows","archived":false,"fork":false,"pushed_at":"2022-12-30T17:29:41.000Z","size":132,"stargazers_count":1,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-14T21:47:55.930Z","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":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cmstead.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":"2019-04-15T21:22:50.000Z","updated_at":"2020-06-09T14:51:12.000Z","dependencies_parsed_at":"2023-01-15T08:17:19.370Z","dependency_job_id":null,"html_url":"https://github.com/cmstead/async-flow-control","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmstead%2Fasync-flow-control","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmstead%2Fasync-flow-control/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmstead%2Fasync-flow-control/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmstead%2Fasync-flow-control/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cmstead","download_url":"https://codeload.github.com/cmstead/async-flow-control/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247273982,"owners_count":20912027,"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":"2024-10-12T07:45:53.369Z","updated_at":"2025-04-05T01:25:43.404Z","avatar_url":"https://github.com/cmstead.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Async Flow Control #\n\nManage complex async flow control problems with ease. Promises and callbacks, async/await with try/catch blocks -- these lead to a substantial amount of noise in the code when the flow control structure gets complex. Conditional execution, looping and the like introduce significant overhead you must keep in mind while you work.\n\nThere can be a better way.\n\nAsync Flow Control (AFC) is a flow control system for handling conditional behaviors in async code. AFC handles callbacks and synchronous calls under a unified interface, without modification or wrappers.\n\nWriting conditional logic in async code can be done in the same number of lines as handling promise resolution:\n\n```javascript\nconst asyncFlowControl = require('async-flow-control');\n\nasyncFlowControl\n    .if(isRemoteFeatureEnabled)\n    .then(promiseReturning)\n\n    .elseIf(isCacheable)\n    .then(cacheRemoteCall)\n\n    .elseSync(fallbackBehavior)\n    \n    .exec()\n    \n    .then(continueExecution)\n    .catch(executeErrorBehavior);\n```\n\n## Setup ##\n\nTo install Async Flow Control, make sure you have a current version of NodeJS installed and run the following command in your project:\n\n`npm i async-flow-control`\n\n## Working With Async Flow Control ##\n\n### Lazy Execution ###\n\nAsync Flow Control is designed to execute behaviors lazily.  This means, the entire setup can be done up front and the actual execution can be done at the appropriate time. By executing lazily, you can construct your intended logical path without triggering an immediate cascade of async execution.\n\nOn the other hand, this means you must explicitly call `exec` to execute your conditional logic. The following example is kind of silly since it doesn't actually do anything, but I think it illustrates the point.\n\n```javascript\n    asyncFlowControl\n        .new()\n        .exec() // This runs your constructed logic\n        \n        .then(doSomethingWhenExecutionCompletes)\n        .catch(errorHandler);\n\n```\n\n### Promises AND Callbacks ###\n\nThe history of async programming in Javascript is long, so we have tools which use callbacks and promises. It's really frustrating to wrap up lots of old API code to match new code patterns. Async Flow Control simply supports both and lets you get on with writing code.\n\nFirst, Async Flow Control will accept functions which conform to the Node.js callback form `callback(error, ...args)`, and standard promises (Promise/A+).\n\n```javascript\n    const checkACondition = \n        () =\u003e Promise.resolve(true);\n    const maybeDoSomething = \n        (callback) =\u003e callback(null, 'It worked!');\n\n    asyncFlowControl\n        .if(checkACondition)\n        .then(maybeDoSomething)\n\n        .exec()\n```\n\nAsync Flow Control also allows you to resolve execution with callbacks or promises.  When exec is called, if a function is provided, it will be treated as a callback. If nothing is passed, it returns a promise:\n\n```javascript\n    // Callback style resolution\n    asyncFlowControl\n        .if(checkACondition)\n        .then(maybeDoSomething)\n\n        .exec((error, ...args) =\u003e \n            console.log('Do stuff here'));\n    \n    // Promise style resolution\n    asyncFlowControl\n        .if(checkACondition)\n        .then(maybeDoSomething)\n\n        .exec()\n\n        .then((...args) =\u003e console.log('Do stuff'))\n        .catch((error) =\u003e console.log('Oh noes!'));\n```\n\n### Simple Conditions ###\n\nAt its core, Async Flow Control is an async conditional handler. This means you can build conditional structures without setting your hair on fire.  This means we can do something like the following:\n\n```javascript\nasyncFlowControl\n\n    .if(checkDataWasUpdated)\n    .then(getLatestData)\n    .then(updateCache)\n\n    .else(getCachedData)\n\n    .exec()\n```\n\nIf, then and else each take a function.  Async Flow Control short circuits execution like a standard conditional, so if `checkDataWasUpdated` returns false, the else function will be executed. All \"thens\" will be skipped.\n\n### Rich Conditions ###\n\nAlthough the if/then/else behavior provides a nice base behavior, it's common for conditional logic to quickly become nested, and hard to manage.  We can refine our logic with `elseIf` like below:\n\n```javascript\nasyncFlowControl\n    .if(cacheIsEmpty)\n    .then(getLatestData)\n    .then(storeDataInCache)\n\n    .elseIf(checkDataWasUpdated)\n    .then(getLatestData)\n    .then(updateCache)\n\n    .else(getCachedData)\n\n    .exec()\n```\n\n### While Loops ###\n\n```javascript\n    let count = 0;\n\n    const checkCount = \n        () =\u003e Promise.resolve(count \u003c 3);\n    const countUpdate = \n        () =\u003e Promise.resolve(++ count);\n\n    asyncFlowControl\n        .while(checkCount)\n        .then(countUpdate)\n\n        .exec();\n```\n\n### Mixing Sync/Async behaviors ###\n\nEvery method provided by Async Flow Control has a \"sync\" counterpart, except `chain` and `exec`.\n\n```javascript\n    const thenBehavior1 =\n        () =\u003e Promise.resolve(5);\n    const thenBehavior2 = \n        value =\u003e \n            Promise.resolve(value + 3);\n\n    return asyncFlowControl\n        .ifSync(() =\u003e true)\n        .then(thenBehavior1)\n        .then(thenBehavior2)\n        .thenSync(value =\u003e value / 2)\n\n        .exec()\n        .then(function (resultSet) {\n            console.log(resultSet); // 4\n        });\n```\n\n### Chaining Functions ###\n\nChaining functions is especially useful when you need to stitch together behaviors. The best case for this is when your functions use promises and callbacks, or when some behaviors are synchronous and other are async:\n\n```javascript\n    const thenBehavior1 = \n        () =\u003e Promise.resolve(5);\n    const thenBehavior2 = \n        (value, callback) =\u003e \n            callback(null, value + 3);\n\n    return asyncFlowControl\n        .chain()\n        .then(thenBehavior1)\n        .then(thenBehavior2)\n        .thenSync(value =\u003e value / 2)\n\n        .exec()\n        .then(function (resultSet) {\n            console.log(resultSet);\n        });\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcmstead%2Fasync-flow-control","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcmstead%2Fasync-flow-control","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcmstead%2Fasync-flow-control/lists"}