New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: await-able Async methods #1572
Changes from 1 commit
0d0c6d1
309dae6
832159b
208ef4d
0c74934
6e07f6c
68363dc
ab7ccd1
e5f01b8
94c411c
ed2eb6c
f12ce39
7df6325
4c53589
4a33ff9
39dd905
01292f5
3a30a9f
0174f3f
f643878
4dbafba
105fd5d
31e68c3
5f71f25
aff6123
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
export const ASYNC_FN = Symbol('asyncFunction') | ||
|
||
// conditionally promisify a function. | ||
// only return a promise if a callback is omitted | ||
export default function awaitify (asyncFn, arity) { | ||
function awaitable(...args) { | ||
if (args.length === arity || typeof args[arity - 1] === 'function') { | ||
return asyncFn.apply(this, args) | ||
} | ||
|
||
return new Promise((resolve, reject) => { | ||
args.push((err, ...cbArgs) => { | ||
if (err) return reject(err) | ||
resolve(cbArgs.length > 1 ? cbArgs : cbArgs[0]) | ||
}) | ||
asyncFn.apply(this, args) | ||
}) | ||
} | ||
|
||
awaitable[Symbol.toStringTag] = 'AsyncFunction' | ||
awaitable[ASYNC_FN] = true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I intend to use this so we can avoid an additional layer of wrapping in |
||
awaitable.displayName = `awaitable(${asyncFn.name})` | ||
|
||
return awaitable | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
const PROMISE_SYMBOL = Symbol('promiseCallback') | ||
|
||
function promiseCallback () { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the older strategy. It's a bit more clunky, but doesn't add another frame to the call stack for each method. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had to use this in a couple places where the |
||
let resolve, reject | ||
function callback (err, ...args) { | ||
if (err) return reject(err) | ||
resolve(args.length > 1 ? args : args[0]) | ||
} | ||
|
||
callback[PROMISE_SYMBOL] = new Promise((res, rej) => { | ||
resolve = res, | ||
reject = rej | ||
}) | ||
|
||
return callback | ||
} | ||
|
||
|
||
export { promiseCallback, PROMISE_SYMBOL } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
var async = require('../../lib'); | ||
const {expect} = require('chai'); | ||
const assert = require('assert'); | ||
|
||
|
||
module.exports = function () { | ||
async function asyncIdentity(val) { | ||
var res = await Promise.resolve(val); | ||
return res; | ||
} | ||
|
||
const input = [1, 2, 3]; | ||
const inputObj = {a: 1, b: 2, c: 3}; | ||
|
||
it('should asyncify async functions', (done) => { | ||
async.asyncify(asyncIdentity)(42, (err, val) => { | ||
assert(val === 42); | ||
done(err); | ||
}) | ||
}); | ||
|
||
|
||
/* | ||
* Collections | ||
*/ | ||
|
||
it('should return a Promise: each', async () => { | ||
const calls = [] | ||
await async.each(input, async val => { calls.push(val) }); | ||
expect(calls).to.eql([1, 2, 3]) | ||
expect(async.each(input, asyncIdentity) instanceof Promise).to.equal(true) | ||
}); | ||
it('should return a Promise: eachSeries', async () => { | ||
const calls = [] | ||
await async.eachSeries(input, async val => { calls.push(val) }); | ||
expect(calls).to.eql([1, 2, 3]) | ||
}); | ||
it('should return a Promise: eachLimit', async () => { | ||
const calls = [] | ||
await async.eachLimit(input, 1, async val => { calls.push(val) }); | ||
expect(calls).to.eql([1, 2, 3]) | ||
}); | ||
|
||
it('should return a Promise: eachOf', async () => { | ||
const calls = [] | ||
await async.eachOf(inputObj, async (...args) => { calls.push(args) }); | ||
expect(calls).to.eql([[1, 'a'], [2, 'b'], [3, 'c']]) | ||
}); | ||
it('should return a Promise: eachOfSeries', async () => { | ||
const calls = [] | ||
await async.eachOfSeries(inputObj, async (...args) => { calls.push(args) }); | ||
expect(calls).to.eql([[1, 'a'], [2, 'b'], [3, 'c']]) | ||
}); | ||
it('should return a Promise: eachOfLimit', async () => { | ||
const calls = [] | ||
await async.eachOfLimit(inputObj, 1, async (...args) => { calls.push(args) }); | ||
expect(calls).to.eql([[1, 'a'], [2, 'b'], [3, 'c']]) | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit of a hack, not sure if we should do this.