Skip to content

Instantly share code, notes, and snippets.

@wentout
Last active June 25, 2025 00:10
Show Gist options
  • Save wentout/004a281ccbc0717f3c756deb2b6d42f5 to your computer and use it in GitHub Desktop.
Save wentout/004a281ccbc0717f3c756deb2b6d42f5 to your computer and use it in GitHub Desktop.
Callback Hell Error-First Callback Convention .bind & .luck solution
'use strict';
/**
AI explanation from Qwen coder 2.5
In Node.js, the conventional way
to perform a null check in a callback function,
particularly for error handling,
involves checking the first argument of the callback.
This is based on the "error-first callback" pattern,
which is a core convention in Node.js asynchronous operations.
Error-First Callback Convention:
First Argument for Error:
The first argument of a Node.js callback function is conventionally reserved for an Error object.
Null on Success:
If the asynchronous operation completes successfully without any errors, this first argument will be null.
Error Object on Failure:
If an error occurs during the operation, an Error object containing details about the error will be passed as the first argument.
Performing the Null Check:
To check for errors within a callback, you simply check if the first argument is not null.
*/
Object.defineProperty(Function.prototype, 'luck', {
get () {
const callabackConcumer = this;
return function (onSuccess) {
return function (mainCallback) {
const callback = function (mayBeError, ...args) {
const self = this;
if (mayBeError instanceof Error) {
return mainCallback.call(self, mayBeError);
}
try {
const result = onSuccess.call(self, mayBeError, ...args);
return mainCallback.call(self, null, result);
} catch (error) {
return mainCallback.call(self, error);
}
};
return callabackConcumer(callback);
};
};
},
enumerable: true
});
Object.defineProperty(Object.prototype, 'bind', {
get () {
return new Proxy(this, {
get (target, prop) {
const result = target[prop];
if (typeof result === 'function') {
return result.bind(target);
}
return result;
}
});
},
enumerable: true
});
Object.defineProperty(Object.prototype, 'luck', {
get () {
return new Proxy(this, {
get (target, prop) {
const result = target[prop];
if (typeof result === 'function') {
return result.bind(target).luck;
}
return result;
}
});
},
enumerable: true
});
const test = {
method_1 (cb) {
console.log('this_1 : ', this);
cb(null, 'test1');
},
method_2 (cb) {
console.log('this_2 : ', this);
cb(null, 'test2');
},
method_3 (cb) {
console.log('this_3 : ', this);
cb(null, 'test3');
},
};
// emulation of caolan async library .parallel method
const runner = function (cb) {
debugger;
// here is a combination of .bind and .luck
(test.bind.method_1.luck(function (error, result) {
console.log('Result 1:', result);
}))(() => { console.log('callback for finishing [ method_1 ]\n')});
// here is just .luck from object prototype, which binds method_2 to test object
(test.luck.method_2(function (error, result) {
console.log('Result 2:', result);
}))(() => { console.log('callback for finishing [ method_2 ]\n')});
// here is no .bind, just .luck from Function prototype, so "this" will be undefined
(test.method_3.luck(function (error, result) {
console.log('Result 3:', result);
}))(() => { console.log('callback for finishing [ method_3 ]\n')});
cb('Finished\n');
};
console.log('\nStarted\n');
debugger;
runner(console.log);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment