mirror of https://github.com/lukechilds/node.git
Browse Source
Add `util.callbackify(function)` for creating callback style functions from functions returning a `Thenable` PR-URL: https://github.com/nodejs/node/pull/12712 Fixes: https://github.com/nodejs/CTC/issues/109 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Teddy Katz <teddy.katz@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Timothy Gu <timothygu99@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>v6
Refael Ackermann
8 years ago
6 changed files with 374 additions and 0 deletions
@ -0,0 +1,15 @@ |
|||
'use strict'; |
|||
|
|||
// Used to test that `uncaughtException` is emitted
|
|||
|
|||
const { callbackify } = require('util'); |
|||
|
|||
{ |
|||
async function fn() { } |
|||
|
|||
const cbFn = callbackify(fn); |
|||
|
|||
cbFn((err, ret) => { |
|||
throw new Error(__filename); |
|||
}); |
|||
} |
@ -0,0 +1,22 @@ |
|||
'use strict'; |
|||
|
|||
// Used to test the `uncaughtException` err object
|
|||
|
|||
const assert = require('assert'); |
|||
const { callbackify } = require('util'); |
|||
|
|||
{ |
|||
const sentinel = new Error(__filename); |
|||
process.once('uncaughtException', (err) => { |
|||
assert.strictEqual(err, sentinel); |
|||
// Calling test will use `stdout` to assert value of `err.message`
|
|||
console.log(err.message); |
|||
}); |
|||
|
|||
async function fn() { |
|||
return await Promise.reject(sentinel); |
|||
} |
|||
|
|||
const cbFn = callbackify(fn); |
|||
cbFn((err, ret) => assert.ifError(err)); |
|||
} |
@ -0,0 +1,227 @@ |
|||
'use strict'; |
|||
const common = require('../common'); |
|||
|
|||
// This test checks that the semantics of `util.callbackify` are as described in
|
|||
// the API docs
|
|||
|
|||
const assert = require('assert'); |
|||
const { callbackify } = require('util'); |
|||
const { join } = require('path'); |
|||
const { execFile } = require('child_process'); |
|||
const fixtureDir = join(common.fixturesDir, 'uncaught-exceptions'); |
|||
const values = [ |
|||
'hello world', |
|||
null, |
|||
undefined, |
|||
false, |
|||
0, |
|||
{}, |
|||
{ key: 'value' }, |
|||
Symbol('I am a symbol'), |
|||
function ok() {}, |
|||
['array', 'with', 4, 'values'], |
|||
new Error('boo') |
|||
]; |
|||
|
|||
{ |
|||
// Test that the resolution value is passed as second argument to callback
|
|||
for (const value of values) { |
|||
// Test and `async function`
|
|||
async function asyncFn() { |
|||
return await Promise.resolve(value); |
|||
} |
|||
|
|||
const cbAsyncFn = callbackify(asyncFn); |
|||
cbAsyncFn(common.mustCall((err, ret) => { |
|||
assert.ifError(err); |
|||
assert.strictEqual(ret, value); |
|||
})); |
|||
|
|||
// Test Promise factory
|
|||
function promiseFn() { |
|||
return Promise.resolve(value); |
|||
} |
|||
|
|||
const cbPromiseFn = callbackify(promiseFn); |
|||
cbPromiseFn(common.mustCall((err, ret) => { |
|||
assert.ifError(err); |
|||
assert.strictEqual(ret, value); |
|||
})); |
|||
|
|||
// Test Thenable
|
|||
function thenableFn() { |
|||
return { |
|||
then(onRes, onRej) { |
|||
onRes(value); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
const cbThenableFn = callbackify(thenableFn); |
|||
cbThenableFn(common.mustCall((err, ret) => { |
|||
assert.ifError(err); |
|||
assert.strictEqual(ret, value); |
|||
})); |
|||
} |
|||
} |
|||
|
|||
{ |
|||
// Test that rejection reason is passed as first argument to callback
|
|||
for (const value of values) { |
|||
// Test an `async function`
|
|||
async function asyncFn() { |
|||
return await Promise.reject(value); |
|||
} |
|||
|
|||
const cbAsyncFn = callbackify(asyncFn); |
|||
cbAsyncFn(common.mustCall((err, ret) => { |
|||
assert.strictEqual(ret, undefined); |
|||
if (err instanceof Error) { |
|||
if ('reason' in err) { |
|||
assert(!value); |
|||
assert.strictEqual(err.code, 'FALSY_VALUE_REJECTION'); |
|||
assert.strictEqual(err.reason, value); |
|||
} else { |
|||
assert.strictEqual(String(value).endsWith(err.message), true); |
|||
} |
|||
} else { |
|||
assert.strictEqual(err, value); |
|||
} |
|||
})); |
|||
|
|||
// test a Promise factory
|
|||
function promiseFn() { |
|||
return Promise.reject(value); |
|||
} |
|||
|
|||
const cbPromiseFn = callbackify(promiseFn); |
|||
cbPromiseFn(common.mustCall((err, ret) => { |
|||
assert.strictEqual(ret, undefined); |
|||
if (err instanceof Error) { |
|||
if ('reason' in err) { |
|||
assert(!value); |
|||
assert.strictEqual(err.code, 'FALSY_VALUE_REJECTION'); |
|||
assert.strictEqual(err.reason, value); |
|||
} else { |
|||
assert.strictEqual(String(value).endsWith(err.message), true); |
|||
} |
|||
} else { |
|||
assert.strictEqual(err, value); |
|||
} |
|||
})); |
|||
|
|||
// Test Thenable
|
|||
function thenableFn() { |
|||
return { |
|||
then(onRes, onRej) { |
|||
onRej(value); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
const cbThenableFn = callbackify(thenableFn); |
|||
cbThenableFn(common.mustCall((err, ret) => { |
|||
assert.strictEqual(ret, undefined); |
|||
if (err instanceof Error) { |
|||
if ('reason' in err) { |
|||
assert(!value); |
|||
assert.strictEqual(err.code, 'FALSY_VALUE_REJECTION'); |
|||
assert.strictEqual(err.reason, value); |
|||
} else { |
|||
assert.strictEqual(String(value).endsWith(err.message), true); |
|||
} |
|||
} else { |
|||
assert.strictEqual(err, value); |
|||
} |
|||
})); |
|||
} |
|||
} |
|||
|
|||
{ |
|||
// Test that arguments passed to callbackified function are passed to original
|
|||
for (const value of values) { |
|||
async function asyncFn(arg) { |
|||
assert.strictEqual(arg, value); |
|||
return await Promise.resolve(arg); |
|||
} |
|||
|
|||
const cbAsyncFn = callbackify(asyncFn); |
|||
cbAsyncFn(value, common.mustCall((err, ret) => { |
|||
assert.ifError(err); |
|||
assert.strictEqual(ret, value); |
|||
})); |
|||
|
|||
function promiseFn(arg) { |
|||
assert.strictEqual(arg, value); |
|||
return Promise.resolve(arg); |
|||
} |
|||
|
|||
const cbPromiseFn = callbackify(promiseFn); |
|||
cbPromiseFn(value, common.mustCall((err, ret) => { |
|||
assert.ifError(err); |
|||
assert.strictEqual(ret, value); |
|||
})); |
|||
} |
|||
} |
|||
|
|||
{ |
|||
// Test that `this` binding is the same for callbackified and original
|
|||
for (const value of values) { |
|||
const iAmThis = { |
|||
fn(arg) { |
|||
assert.strictEqual(this, iAmThis); |
|||
return Promise.resolve(arg); |
|||
}, |
|||
}; |
|||
iAmThis.cbFn = callbackify(iAmThis.fn); |
|||
iAmThis.cbFn(value, common.mustCall(function(err, ret) { |
|||
assert.ifError(err); |
|||
assert.strictEqual(ret, value); |
|||
assert.strictEqual(this, iAmThis); |
|||
})); |
|||
|
|||
const iAmThat = { |
|||
async fn(arg) { |
|||
assert.strictEqual(this, iAmThat); |
|||
return await Promise.resolve(arg); |
|||
}, |
|||
}; |
|||
iAmThat.cbFn = callbackify(iAmThat.fn); |
|||
iAmThat.cbFn(value, common.mustCall(function(err, ret) { |
|||
assert.ifError(err); |
|||
assert.strictEqual(ret, value); |
|||
assert.strictEqual(this, iAmThat); |
|||
})); |
|||
} |
|||
} |
|||
|
|||
{ |
|||
// Test that callback that throws emits an `uncaughtException` event
|
|||
const fixture = join(fixtureDir, 'callbackify1.js'); |
|||
execFile( |
|||
process.execPath, |
|||
[fixture], |
|||
common.mustCall((err, stdout, stderr) => { |
|||
assert.strictEqual(err.code, 1); |
|||
assert.strictEqual(Object.getPrototypeOf(err).name, 'Error'); |
|||
assert.strictEqual(stdout, ''); |
|||
const errLines = stderr.trim().split(/[\r\n]+/g); |
|||
const errLine = errLines.find((l) => /^Error/.exec(l)); |
|||
assert.strictEqual(errLine, `Error: ${fixture}`); |
|||
}) |
|||
); |
|||
} |
|||
|
|||
{ |
|||
// Test that handled `uncaughtException` works and passes rejection reason
|
|||
const fixture = join(fixtureDir, 'callbackify2.js'); |
|||
execFile( |
|||
process.execPath, |
|||
[fixture], |
|||
common.mustCall((err, stdout, stderr) => { |
|||
assert.ifError(err); |
|||
assert.strictEqual(stdout.trim(), fixture); |
|||
assert.strictEqual(stderr, ''); |
|||
}) |
|||
); |
|||
} |
Loading…
Reference in new issue