From e7948013f3c3741606159e0c86f03dbb9e20ff2b Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Sun, 28 Jun 2015 00:10:40 -0700 Subject: [PATCH] added async.asyncify. closes #671 --- CHANGELOG.md | 1 + README.md | 25 +++++++++++++++++++++++++ lib/async.js | 15 +++++++++++++++ test/test-async.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2c53aace..1eaf76a08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ New Features: - Added `constant` +- Added `asyncify`/`wrapSync` for making sync functions work with callbacks. (#671, #806) - `retry` now accepts an `interval` parameter to specify a delay between retries. (#793) - `async` should work better in Web Workers due to better `root` detection (#804) - Callbacks are now optional in `whilst`, `doWhilst`, `until`, and `doUntil` (#642) diff --git a/README.md b/README.md index 2ec6626aa..0197f1554 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,8 @@ Usage: * [`unmemoize`](#unmemoize) * [`ensureAsync`](#ensureAsync) * [`constant`](#constant) +* [`asyncify`](#asyncify) +* [`wrapSync`](#wrapSync) * [`log`](#log) * [`dir`](#dir) * [`noConflict`](#noConflict) @@ -1785,6 +1787,29 @@ async.auto({ --------------------------------------- + + +### asyncify(func) + +*Alias: wrapSync* + +Take a sync function and make it async, passing its return value to a callback. This is useful for plugging sync functions into a waterfall, series, or other async functions. Any arguments passed to the generated function will be passed to the wrapped function (except for the final callback argument). Errors thrown will be passed to the callback. + +__Example__ + +```js +async.waterfall([ + async.apply(fs.readFile, filename, "utf8"), + async.asyncify(JSON.parse), + function (data, next) { + // data is the result of parsing the text. + // If there was a parsing error, it would have been caught. + } +], callback) +``` + +--------------------------------------- + ### log(function, arguments) diff --git a/lib/async.js b/lib/async.js index 0da106289..6191b576e 100644 --- a/lib/async.js +++ b/lib/async.js @@ -1221,6 +1221,21 @@ }; }; + async.wrapSync = + async.asyncify = function asyncify(func) { + return function (/*args..., callback*/) { + var args = _baseSlice(arguments); + var callback = args.pop(); + var result; + try { + result = func.apply(this, args); + } catch (e) { + return callback(e); + } + callback(null, result); + }; + }; + // Node.js if (typeof module !== 'undefined' && module.exports) { module.exports = async; diff --git a/test/test-async.js b/test/test-async.js index 8b89f78e2..321694a96 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -4069,3 +4069,48 @@ exports['constant'] = function (test) { test.done(); }); }; + +exports['asyncify'] = { + 'asyncify': function (test) { + var parse = async.asyncify(JSON.parse); + parse("{\"a\":1}", function (err, result) { + test.ok(!err); + test.ok(result.a === 1); + test.done(); + }); + }, + + 'variable numbers of arguments': function (test) { + async.asyncify(function (x, y, z) { + test.ok(arguments.length === 3); + test.ok(x === 1); + test.ok(y === 2); + test.ok(z === 3); + })(1, 2, 3, function () {}); + test.done(); + }, + + 'catch errors': function (test) { + async.asyncify(function () { + throw new Error("foo"); + })(function (err) { + test.ok(err); + test.ok(err.message === "foo"); + test.done(); + }); + }, + + 'dont catch errors in the callback': function (test) { + try { + async.asyncify(function () {})(function (err) { + if (err) { + return test.done(new Error("should not get an error here")); + } + throw new Error("callback error"); + }); + } catch (e) { + test.ok(e.message === "callback error"); + test.done(); + } + } +};