Last active
June 25, 2025 00:10
-
-
Save wentout/004a281ccbc0717f3c756deb2b6d42f5 to your computer and use it in GitHub Desktop.
Callback Hell Error-First Callback Convention .bind & .luck solution
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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