Last active
April 23, 2017 14:07
-
-
Save softwarespot/50b5e8cbf58d8999008a7128c64b8e43 to your computer and use it in GitHub Desktop.
Chainer implementation
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
// Create an object for checking if an internal object error/value combination or a simple value | |
var _internal = { | |
name: 'Chainer' | |
}; | |
function Chainer(initial) { | |
var internal = _isInternal(initial) ? | |
initial : | |
_createInternal(null, initial); | |
var fn = _isFunction(arguments[1]) ? | |
arguments[1] : | |
function () { | |
return internal.value; | |
}; | |
// Any other value i.e. not a Promise-like object | |
if (!_isPromise(internal.value)) { | |
this.value = _tryFunction(fn, this, internal.error, internal.value); | |
return; | |
} | |
this.value = internal.value | |
.then(function (value) { | |
var internal = _isInternal(value) ? | |
value : | |
_createInternal(null, value); | |
// Invoke the next pipe function | |
return _tryFunction(fn, this, internal.error, internal.value); | |
}) | |
.catch(function (err) { | |
// Pass an internal object through to the next "thenable", which will invoke the function | |
return _createInternal(err, undefined); | |
}); | |
} | |
// Create a configuration object for "Chainer", whereby the Promise function can be | |
// specified | |
Chainer.config = { | |
Promise: Promise | |
}; | |
Chainer.prototype = { | |
async: function (fn) { | |
var promise = new Chainer.config.Promise(function (success, error) { | |
try { | |
// Inverse the order to match that of when passing the "error" value | |
// and "success" value to the piped function | |
fn(error, success); | |
} catch (ex) { | |
error(ex); | |
} | |
}); | |
return new Chainer(promise, function (err, value) { | |
// Check if no error ocurred inside the async pipe | |
return err === null ? | |
value : | |
err; | |
}); | |
}, | |
pipe: function (fn) { | |
return new Chainer(this.value, fn); | |
} | |
}; | |
// Helper functions | |
// Create an internal object containing an error/value combination | |
function _createInternal(err, value) { | |
return { | |
error: err, | |
value: value, | |
_internal: _internal | |
}; | |
} | |
// Wrapper to check if an argument is a function data type | |
function _isFunction(fn) { | |
return typeof fn === 'function'; | |
} | |
// Wrapper for checking if an error-like argument is an internal error/value object | |
function _isInternal(obj) { | |
return obj && obj._internal === _internal; | |
} | |
// Wrapper for checking if an object is Promise-like i.e. contains a "thenable" function | |
function _isPromise(promise) { | |
return promise && typeof promise.then === 'function'; | |
} | |
// Wrapper to invoke a callback function safely and return an internal error/value object | |
function _tryFunction(fn, context /* , args */) { | |
var error = null; | |
var value; | |
try { | |
// Remove the callback function and context arguments | |
value = fn.apply(context, Array.from(arguments).slice(2)); | |
} catch (ex) { | |
error = ex; | |
} | |
return _createInternal(error, value); | |
} | |
// Chainer Example(s) | |
(new Chainer('A')) | |
.pipe(function (err, value) { | |
console.log(err, value); | |
return 'B'; | |
}) | |
.pipe(function (err, value) { | |
console.log(err, value); | |
return 'C'; | |
}) | |
.pipe(function (err, value) { | |
console.log(err, value); | |
// Can handle Promises, thus making all further pipes async | |
return new Promise(function (resolve) { | |
setTimeout(resolve.bind(null, 'D'), 200); | |
}); | |
}) | |
.pipe(function (err, value) { | |
console.log(err, value); | |
throw new Error('An unexpected error occurred with inside this pipe'); | |
}) | |
.pipe(function (err, value) { | |
if (err) { | |
console.log(err); | |
// Return "E" as the value, since the next pipe will handle this as a "default" value instead | |
return 'E'; | |
} | |
console.log(value); | |
return 'F'; | |
}) | |
.pipe(function (err, value) { | |
console.log(err, value); | |
}); | |
(new Chainer('Z')) | |
.async(function (error, success) { | |
setTimeout(success.bind(null, 'Y'), 200); | |
return 'ignore the following return statement value'; | |
}) | |
.pipe(function (err, value) { | |
console.log(err, value); | |
return 'X'; | |
}) | |
.pipe(function (err, value) { | |
console.log(err, value); | |
return 'W'; | |
}). | |
pipe(function (err, value) { | |
console.log(err, value); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment