{"id":13519561,"url":"https://github.com/creationix/safereturn","last_synced_at":"2025-04-10T02:43:59.506Z","repository":{"id":3010757,"uuid":"4029798","full_name":"creationix/safereturn","owner":"creationix","description":"Functional helpers for callback based code.","archived":false,"fork":false,"pushed_at":"2012-04-19T22:05:54.000Z","size":133,"stargazers_count":38,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-24T04:14:36.890Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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/creationix.png","metadata":{"files":{"readme":"README.markdown","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":"2012-04-15T04:27:35.000Z","updated_at":"2023-09-08T16:33:02.000Z","dependencies_parsed_at":"2022-09-04T11:42:00.151Z","dependency_job_id":null,"html_url":"https://github.com/creationix/safereturn","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/creationix%2Fsafereturn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/creationix%2Fsafereturn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/creationix%2Fsafereturn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/creationix%2Fsafereturn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/creationix","download_url":"https://codeload.github.com/creationix/safereturn/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248145540,"owners_count":21055152,"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-08-01T05:02:00.535Z","updated_at":"2025-04-10T02:43:59.480Z","avatar_url":"https://github.com/creationix.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# SafeReturn\n\nSafeReturn is a tiny script that contains functional helpers for callback based code.\n\n## Purpose\n\nTo make writing vanilla callback based code more enjoyable.\n\nThis package eases the pain, but still allows using free-form callbacks.\n\nLibraries that attempt to manage your control-flow for you are doing it wrong.\nInstead SafeReturn makes it easier to do it yourself the right way. \n\nFunctions can be defined anywhere.  They are just values.  They close over\ntheir local state and can carry it with them.  This is very powerful and simple!\n\nWell formed async functions obey some additional rules:\n\n - Async functions must return either a value or error in the callback.\n - The callback must eventually fire and only once.\n - Async function must never throw exceptions except for argument errors.\n - ...\n\nWriting the boilerplate to check these conditions is tedious and error-prone.\nSafeReturn wants to help.\n\n## Non-Goals\n\n - Not require any special syntax, function block, or indentation.  Must only\n   consume a small amount of vertical space.\n - Not appear to be blocking or sequential in any way.  Better to embrace callbacks.\n\n## API\n\n### `safeReturn(callback, timeout) -\u003e wrappedCallback`\n\nThis function is the main API.  It takes in your callback and returns a safe wrapped\nversion of the callback.  Optional is a timeout override for this wrapping.  The global\ndefault timeout can be changed at `safeReturn.defaultTimeout`.  A timeout of `0`\nmeans to disable the timeout.\n\nThis wrapped function will only fire once and (if timers are enabled) always fire.\n\n```js\nfunction funkyStatFile(path, callback) {  \n  callback = safeReturn(callback); // Make sure our callback *always* fires once.\n  if (Math.random() \u003e 0.92) return; // Sometimes it just doesn't work right?\n  fs.stat(path, callback); // Make the real call.\n  if (Math.random() \u003e 0.92) fs.stat(path, callback); // Twice for fun, right?\n}\n```\n\n### `safeReturn.map(callback, length) -\u003e itemCallbackGenerator`\n\nThis is for functions that want to do `length` async operations in parallel.\nIt takes the callback and the number of items you expect to do in parallel.  It\nreturns a function that should be called in each loop iteration (passing in the\nindex key) to get the callback for that item.\n\n```js\nfunction readdir(path, callback) {\n  callback = safeReturn(callback, 0); // Wrap, but disable timeout\n  fs.readdir(path, function (err, names) {\n    if (err) return callback(err); // If readdir failed, we're done.  Abort.\n    // Use the map helper to call our callback once names.length item callbacks fire.\n    var onItem = safeReturn.map(callback, names.length);\n    names.forEach(function (name) {\n      // Note that onItem isn't the actual callback, but returns one when called.\n      funkyStatFile(path + \"/\" + name, onItem(name));\n    });\n  });\n}\n```\n\nNote that map doesn't duplicate any of the checks from safeReturn. It's \nrecommended to use in conjuction with safeReturn.\n\n## Techniques\n\nYep, that's the whole API.  The real power comes in techniques.  Remember a goal\nof SafeReturn is to not reimplement all possible control-flow patterns, but to\nenable and assist you the programmer.\n\n### Serial Chain\n\nSometimes you want to start some async action, wait for it to finish, and then\ndo something else.  This is easiest done with nesting.  \n\n```js\nfunction myAction(param, callback) {\n  callback = safeReturn(callback);\n  doSomething(param, function (err, data) {\n    if (err) return callback(err);\n    // Notice we used data and param from the closure.  Nesting is great for simple stuff.\n    doAnother(data, param, callback);\n  });\n}\n```\n\nIt can also be accomplished with named functions.\n\n```js\nfunction folderInfo(path, callback) {\n  callback = safeReturn(callback);\n  // Note that we can wrap callbacks we give to other code as well as the ones\n  // we're given.  I trust fs.readdir to behave, but if it was some other lib\n  // I might not trust it and want to wrap the callback.\n  fs.readdir(path, safeReturn(onReaddir)); \n  \n  function onReaddir(err, files) {\n    if (err) return callback(err);\n    var onItem = safeReturn.map(onStats, files.length);\n    files.forEach(function (name) {\n      // Here we wrap the onItem callback to ensure all of them fire exactly once\n      // This will ensure out next step eventually fires.\n      fs.stat(path + \"/\" + name, safeReturn(onItem(name)));\n    });\n  }\n  \n  function onStats(err, obj) {\n    if (err) return callback(err);\n    // Massage the data format to be what we want to output.\n    var results = {};\n    var errors = {};\n    Object.keys(obj).forEach(function (name) {\n      var value = obj[name];\n      if (value[0]) errors[name] = value[0];\n      else results[name] = value[1];\n    });\n    callback(null, errors, results);\n  }\n}\n```\n\n### Branching and other Advanced Topics\n\nBranching or other advanced techniques can still be done unlike in Step.  \nRemember this is freeform JavaScript.  All functions in SafeReturn accept the\ncallback (or next step) as input.  This means you can route logic in whatever\ndirection you want. SafeReturn is very flexible.  Come up with some really neat\nexamples and send a pull request to have them added here.\n\n(See full examples in the examples folder)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcreationix%2Fsafereturn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcreationix%2Fsafereturn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcreationix%2Fsafereturn/lists"}