How to handle a duplicate function call from the arrays of function? - javascript

I am trying to execute following array (avoid callbackHell) of functions(sync/async), in a sequential order, implementing function runCallbacksInSequence (I need to implement my own function to understand how callbacks work and avoid using Async.js).
Here is what I have so far. The function runCallbacksInSequence works well till it gets the same callback more than once. It stops and does not continue to execute the next callback. Ideally if it gets the same callback more than once it should not execute it second time and continue with the next callback.
If you have any ideas let me know what I am doing wrong and how I can fix it.
- no promises and async/await
function first(cb) {
setTimeout(function() {
console.log('first()');
cb(null, 'one');
}, 0);
}
function second(cb) {
setTimeout(function() {
console.log('second()');
cb(null, 'two');
}, 100);
}
function third(cb) {
setTimeout(function() {
console.log('third()');
cb(null, 'three');
}, 0);
}
function last(cb) {
console.log('last()');
cb(null, 'lastCall');
}
const cache = {};
function runCallbacksInSequence(fns, cb) {
fns.reduce(
function(r, f) {
return function(k) {
return r(function() {
if (cache[f]) {
return;
// f(function(e, x) {
// e ? cb(e) : k(x);
// });
} else {
cache[f] = f;
return f(function(e, x) {
return e ? cb(e) : k(x);
});
}
});
};
},
function(k) {
return k();
}
)(function(r) {
return cb(null, r);
});
}
const fns = [first, second, third, second, last];
runCallbacksInSequence(fns, function(err, results) {
if (err) return console.log('error: ' + err.message);
console.log(results);
});

Your function chaining depends on the call to k(). Therefore in your cache logic:
if (cache[f]) {
return;
} else {
// ...
The chain breaks.
What you want instead is this:
if (cache[f]) {
return k();
} else {
// ...
Alternative Implementation
One of the problems with the nested function implementation is that it is hard to reason about due to multiple nesting scopes (and multiple functions being juggled at once (r, f, k, cb).
A simpler approach to this is rather than trying to programmatically build callback hell you can use a queue instead (which is what async.js does). The idea is simple, pop() or shift() functions from an array until the array is empty:
function runCallbacksInSequence(fns, cb) {
let result = [];
let cache = {};
function loop () {
if (fns.length > 0) {
let f = fns.shift(); // remove one function from array
if (cache[f]) {
loop(); // skip this round
return;
}
cache[f] = f;
f(function(err, val) {
if (!err) {
result.push(val); // collect result
loop();
}
else {
// Handle errors however you want.
// Here I'm just terminating the sequence:
cb(err, result);
}
});
}
else {
cb(null, result); // we've collected all the results!!
}
}
loop(); // start the loop
}
As you can see, it's fairly easy to implement any flow logic with this structure. We can easily implement things like waterfall, parallelLimit etc. by controlling how we keep track of results and how many functions we remove from the array per iteration.

I guess with implementation based on cache you may omit doubled step with a direct k() invocation.
return;
if (cache[f]) {
return;
// f(function(e, x) {
// e ? cb(e) : k(x);
// });
Idea:
if (cache[f]) {
return k(function(e, x) {
return e ? cb(e) : k(x);
});

Your code is a little bit hard to read for me. So here is the alternative solution:
<script>
// The data
function first(cb) {
setTimeout(function () {
console.log('first()');
cb(null, 'one');
}, 0);
}
function second(cb) {
setTimeout(function () {
console.log('second()');
cb(null, 'two');
}, 100);
}
function third(cb) {
setTimeout(function () {
console.log('third()');
cb(null, 'three');
}, 0);
}
function last(cb) {
console.log('last()');
cb(null, 'lastCall');
}
const fns = [first, second, third, second, last];
// We need hash function to create the identifyer of the function
function hashCode(str) {
return Array
.from(str)
.reduce((s, c) => Math.imul(31, s) + c.charCodeAt(0) | 0, 0);
}
const cache = [];
function reducer(accumulator, currentFunction) {
// Take the functon string representation to detect "the same function"
const hash = hashCode(currentFunction.toString());
// Process the result of the current function and call the next one.
// We use "reduceRight" so `accumulator` is the next function in the chain.
const cb = function (fp, result) {
console.log(result);
// Cache the result;
cache[hash] = result;
accumulator();
}
// Run just a callback if we already have the result of the current function
return () => cache[hash] ? cb(null, cache[hash]) : currentFunction(cb);
}
fns.reduceRight(reducer, () => { })();
</script>
Result:
first()
one
second()
two
third()
three
two
last()
lastCall
If you do not want to process the cached result at all, then replace the call to the callback with the call to the accumulator directly.
return () => cache[hash] ? cb(null, cache[hash]) : currentFunction(cb);
replace with:
return () => cache[hash] ? accumulator() : currentFunction(cb);
Result:
first()
one
second()
two
third()
three
last()
lastCall
Solution without cache
It is much cleaner:
<script>
// Use the same data as in the example with cache
function reducer(accumulator, currentFunction) {
const cb = function (fp, result) {
console.log(result);
accumulator();
}
return () => currentFunction(cb)
}
fns.reduceRight(reducer, () => { })();
</script>
Result:
first()
one
second()
two
third()
three
second()
two
last()
lastCall

Related

What's the alternative to the reduce pattern with Promises when dealing with recursion?

Note: I cannot use async.
I like to use the reduce pattern in cases where I need to run over an array and execute the same function on its members and return a promise, like so:
function get_count() {
return new Promise(function(resolve, reject) {
resolve(3);
});
}
function recursively_execute(data) {
return new Promise(function(resolve, reject) {
resolve(data);
});
}
function reduce_promise_pattern() {
const get_batch_run_count = get_count();
const batch_process = get_batch_run_count.then((count_value) => {
const run_count = new Array(count_value).fill('batch');
function recursive_function(data) {
console.log('Running batch!');
return recursively_execute(data).then(() => {
return data;
});
}
return run_count.reduce((previous_promise) => {
return previous_promise.then((previous_response) => {
test_data = {
'test': 1
};
return recursive_function(test_data);
})
}, Promise.resolve())
});
return batch_process;
}
This will run 3 times because of the run_count which basically builds an array of 3 items. Although it works, this feels like a hack to me.
This approach works when my list is already pre-defined with unique items and these items, well, individually are used inside that reduce as data that is built upon for example, if I have 3 steps to go through, these 3 steps are all unique and each step's data will be used within that one run...but in my case? I'm just tricking the system to think these are different items.
What is the alternative to this?
You reached the limits of Promise chains, although they work they ain't readable. That's why async / await was introduced to handle exactly these usecases, with them you can just halt all kinds of (nested) loops without having to maintain promises for each:
async function reducePromisePattern() {
for(let i = await getCount(); i >= 0; i--) {
await recursiveFunction({'test': 1 });
}
}
If you can't use / transpile async, you could still write some small helpers to do the looping for you, e.g.:
function loopAsync(times, fn) {
function task() {
times--;
if(times <= 0) return;
return fn().then(task);
}
return Promise.resolve().then(task);
}
function reducePromisePattern() {
return getCount().then(function(count) {
return asyncLoop(count, function() {
return recursiveFunction({ test: 1 });
});
});
}
Here are two options without nesting functions in one another. The first one simply uses a for-loop while the second function uses a recursive solution. The last argument of both solutions is optional and should only be used if you want to pass the return data forward from one run to the next (similar to reduce).
const sleep = () => new Promise(resolve => setTimeout(resolve, Math.random() * 1500 + 500));
// solution #1 - for-loop
function times1(n, callback, init) {
var promise = Promise.resolve(init);
for (; n > 0; --n) {
promise = promise.then(val => callback(val));
}
return promise;
}
// example usage
times1(3, n => {
console.log("solution #1 -", n);
return sleep().then(() => n + 1);
}, 0);
// solution #2 - recursive
function times2(n, callback, init) {
var promise = Promise.resolve(init);
if (n <= 0) return promise;
return promise.then(val => times2(n - 1, callback, callback(val)));
}
// example usage
times2(3, n => {
console.log("solution #2 -", n);
return sleep().then(() => n + 1);
}, 0);

How to use common try-catch for processing every given function in Javascript?

These are some of my functions, I need to write a common function to see if functions are running without error. I tried with try/catch method. But I could only do that individually on each function.
function fisrt(){
console.log("First");
};
function second(){
console.log("Second");
}
function third(){
console.log("Third");
}
fisrt();
second();
third();
I was writing each function inside the try-catch. Is there a way I can write a common try-catch for all the functions.
try {
(function first() {
console.log("ffgdf")
})();
}catch (e) {
console.log( "won't work" );
}
You could define a wrapper function, that takes your desired function as a parameter, and wraps it in a try catch.
function wrapper(fn) {
try {
fn();
} catch(error) {
console.error(error);
}
}
Then given your original functions:
function first() {
console.log("First");
};
function second() {
console.log("Second");
}
function third() {
console.log("Third");
}
You can test each one by using your wrapper function:
wrapper(first);
wrapper(second);
wrapper(third);
Without needing to add try catch to each function.
Regarding the introducing sentence of the accepted answer two years ago ...
You could define a wrapper function, that takes your desired function as a parameter, and wraps it in a try catch.
... this part could be covered with (an) abstraction(s) where one also can provide the (different) handling of an invocation failure (the catch and exception clause).
If one does, for instance, provide functionality that wraps a function/method in way that does not only provide the try catch but also takes the exception handling into account, one can easily accomplish tasks, like the one asked by the OP for, which are mainly about programmable approaches for automatically creating and processing lists of functions/methods that are going to be invoked whilst suppressing unintended/unexpected invocation failures.
The following example does implement afterThrowing and afterFinally, two method modifier methods, each processing the original function/method with an exception handler as its first argument.
Making use of the provided abstraction(s) the approach itself boils down to writing reduce functionality which processes an array of functions by invoking each function with its own set of arguments and collecting its invocation success state ...
function first(...args) {
console.log("first :: does succeed :: argsList :", args);
return args.join(', ');
}
function second(...args) {
console.log("second :: going to fail :: argsList :", args);
throw new Error('2nd invocation failed.');
}
function third(...args) {
console.log("third :: going to fail :: argsList :", args);
throw new Error('3rd invocation failed.');
}
function fourth(...args) {
console.log("fourth :: does succeed :: argsList :", args);
return args.join(', ');
}
function fifth(...args) {
console.log("fifth :: does succeed :: argsList :", args);
return args.join(', ');
}
/**
* reduce functionality which processes an array of functions.
*/
function collectResultAfterThrowing(collector, fct, idx) {
function afterThrowingHandler(error, argsArray) {
// - can access the try-catch exception and the arguments
// that have been passed prior to the invocation failure.
return {
success: false,
failure: {
message: error.toString(),
argsList: Array.from(argsArray)
}
}
}
function unifyResult(value) {
return ((
value
&& value.hasOwnProperty('success')
&& (value.success === false)
&& value
) || { success: true, value });
}
collector.results.push(
unifyResult(fct // - modify original function towards an
.afterThrowing(afterThrowingHandler) // `afterThrowing` handling of its try-catch result(s).
.apply(null, collector.listOfArguments[idx])
)
// - an `afterThrowing` modified function does always return either the result of the
// original function's invocation or the return value of its 'afterThrowing' handler.
);
return collector;
}
/**
* reduce functionality which processes an array of functions.
*/
function collectResultAfterFinally(collector, fct, idx) {
function isError(type) {
return (/^\[object\s+Error\]$/).test(Object.prototype.toString.call(type));
}
function createResult(value) {
return (isError(value) && {
success: false,
message: value.toString()
} || {
success: true,
value
});
}
collector.results.push(
createResult(fct // - modify original function towards an
.afterFinally(() => null) // `afterFinally` handling of its try-catch result(s).
.apply(null, collector.listOfArguments[idx])
)
// - an `afterFinally` modified function does always return either the result of the
// original function's invocation or the try-catch exception of the invocation attempt.
);
return collector;
}
// ... two times, each the actual task,
// ... once based on "afterThrowing" and
// ... once based on "afterFinally" ...
console.log('"afterThrowing" based try-and-catch results :', [
first,
second,
third,
fourth,
fifth
].reduce(collectResultAfterThrowing, {
listOfArguments: [
['foo', 'bar'],
['baz', 'biz'],
['buz', 'foo'],
['bar', 'baz'],
['biz', 'buz']
],
results: []
}).results
);
console.log('\n\n\n');
console.log('"afterFinally" based try-and-catch results :', [
first,
second,
third,
fourth,
fifth
].reduce(collectResultAfterFinally, {
listOfArguments: [
['foo', 'bar'],
['baz', 'biz'],
['buz', 'foo'],
['bar', 'baz'],
['biz', 'buz']
],
results: []
}).results
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
(function (Function) {
const fctPrototype = Function.prototype;
const FUNCTION_TYPE = (typeof Function);
function isFunction(type) {
return (
(typeof type == FUNCTION_TYPE)
&& (typeof type.call == FUNCTION_TYPE)
&& (typeof type.apply == FUNCTION_TYPE)
);
}
function getSanitizedTarget(target) {
return ((target != null) && target) || null;
}
function afterThrowing/*Modifier*/(handler, target) {
target = getSanitizedTarget(target);
const proceed = this;
return (
isFunction(handler) &&
isFunction(proceed) &&
function () {
const context = target || getSanitizedTarget(this);
const args = arguments;
let result;
try {
result = proceed.apply(context, args);
} catch (exception) {
result = handler.call(context, exception, args);
}
return result;
}
) || proceed;
}
// afterThrowing.toString = () => 'afterThrowing() { [native code] }';
function afterFinally/*Modifier*/(handler, target) {
target = getSanitizedTarget(target);
const proceed = this;
return (
isFunction(handler) &&
isFunction(proceed) &&
function () {
const context = target || getSanitizedTarget(this);
const args = arguments;
let result, error;
try {
result = proceed.apply(context, args);
} catch (exception) {
error = exception;
} // finally { ... }
result = (error || result);
handler.call(context, result, args);
return result;
}
) || proceed;
}
// afterFinally.toString = () => 'afterFinally() { [native code] }';
Object.defineProperty(fctPrototype, 'afterThrowing', {
configurable: true,
writable: true,
value: afterThrowing/*Modifier*/
});
Object.defineProperty(fctPrototype, 'afterFinally', {
configurable: true,
writable: true,
value: afterFinally/*Modifier*/
});
}(Function));
</script>
In case of you wishing to get a "throwed" exception, you can use the Promise.all.
function parent() {
function first() {
console.log('First');
throw new Error('First error');
}
function second() {
console.log('Second');
}
function third() {
console.log('Third');
}
return Promise.all([
first(),
second(),
third(),
])
}
parent()
.then(result => console.log(result))
.catch(error => console.error(error))

How to make array.forEach(asyncfn) synchronized?

Say I have an array and I want to perform an async-function on each element of the array.
let a = [x1, x2, x3]
// I want to
await a.forEach(async (x) => {...})
// which equals to
let fn = async (x) => {...}
await fn(x1)
await fn(x2)
await fn(x3)
How can I do this?
Like this?
for (let x of a) {
await fn(x);
}
Or if you really dislike creating a separate fn:
for (let x of a) {
await (async v => {
...
})(x);
}
You could even add it to Array.prototype:
Array.prototype.resolveSeries = async function(fn) {
for (let x of this) {
await fn(x);
}
}
// Usage:
await a.resolveSeries(fn);
// Or:
await a.resolveSeries(async x => {
...
});
I have used a library called async. There a function called eachSeries. It takes an array, an async function, and a callback. The function is called on each item for the array.
This question can open a rabbit hole of complexity. You will need to be careful that your async function doesn't make an asynchronous call. The library provides a callback that can be useful in this case.
function asyncFunction(val, callback) {
return (function() {
//do stuff
callback();
})();
}
The callback will initiate the a call on the next item in the array.
I've been using this function for a while, in case if a dedicated library is not desired:
// usage:
// array.asyncEach(function(item, resume) {
// do something...
// console.log(item);
// resume(); // call `resume()` to resume the cycle
// }
//
// unless resume() is called, loop doesn't proceed to the next item
Array.prototype.asyncEach = function(iterator) {
var list = this,
n = list.length,
i = -1,
calls = 0,
looping = false;
var iterate = function() {
calls -= 1;
i += 1;
if (i === n) return;
iterator(list[i], resume);
};
var loop = function() {
if (looping) return;
looping = true;
while (calls > 0) iterate();
looping = false;
};
var resume = function() {
calls += 1;
if (typeof setTimeout === 'undefined') loop();
else setTimeout(iterate, 1);
};
resume();
};
Perform any async tasks inside the function, and call resume() when you are done.
I don't remember where did I get this function from.
Is there any specific event on which these function will be called ?
if yes can be achieved through **closure** in javascript.
right now your function will be called with last value in array when you invoke it

javascript recursive class: undefined method

I have a JavaScript class that is meant to help deal with promises. First you add functions to an array, then it executes them pops them and calls itself to do the next one. At the end of the array it resolves that promise. My hope was to then propagate the resolution all the way up the stack of recursive calls. This will allow you to force multiple asynchronous functions to run sequentially using a simple set of commands. furthermore employ logic to modify the flow of the ansync functions.
function Sequencer() {
this.functionSequence = [];
this.addFunction = function (func) {
this.functionSequence.push(func);
}
this.getFunctionSequence = function () {
return functionSequence;
}
this.executeAll = function () {
var functionList = this.functionSequence;
var deferred = $q.defer();
if (functionList.length > 0) {
functionList[0]().then(function (result) {
if (result) {
functionList.splice(0, 1);
executeAll().then(function (resultInner) {
if (resultInner == true) {
deferred.resolve(true);
} else {
deferred.resolve(false);
}
});
} else {
functionList = [];
deferred.resolve(false);
}
});
} else {
deferred.resolve(true);
}
return deferred.promise;
}
}
I am getting ReferenceError: 'executeAll' is undefined
in this script, on the recursive call line "executeAll' just after the splice
the first function in the array is being executed(I was testing it with a modal pop up) and when it resolves it hits the splice, then it throws the error right on the executeAll line. Am I defining the function incorrectly? Am I calling it correctly as a recursive function?
use this.executeAll - assuming this will be correct, which it wont, so you'll need to account for that as well ... something like var self = this at the top of executeAll, then call self.executeAll
this.executeAll = function() {
var functionList = this.functionSequence;
var deferred = $q.defer();
var self = this; // save reference to this
if (functionList.length > 0) {
functionList[0]().then(function(result) {
if (result) {
functionList.splice(0, 1);
// need to use self here because "this" is not the "this" we want
self.executeAll().then(function(resultInner) {
if (resultInner == true) {
deferred.resolve(true);
} else {
deferred.resolve(false);
}
});
} else {
functionList = [];
deferred.resolve(false);
}
});
} else {
deferred.resolve(true);
}
return deferred.promise;
};
The reason this is not the this you "want" is due to how this works in javascript - there is plenty on info on stack exchange about using this - I'll find and link a good answer shortly
I offer this alternative code
this.executeAll = function() {
return this.functionSequence.reduce(function(promise, item) {
return promise.then(function(result) {
if (result) {
return item();
}
else {
throw "Fail"; // throw so we stop the chain
}
});
}, Promise.resolve(true))
.then(function(result) {
this.functionSequence = []; // clear out the added functions
return true; // fulfilled value is true as per original code
}.bind(this), function(err) {
this.functionSequence = []; // clear out the added functions
if (err == "Fail") {
return false; // convert the "Fail" to a fullfilled value of false as per original code
}
else {
throw err; // any other error - re-throw the error
}
}.bind(this))
};

Concept - Distilling how a promise works?

I've looked at many implementations and they all look so different I can't really distill what the essence of a promise is.
If I had to guess it is just a function that runs when a callback fires.
Can someone implement the most basic promise in a few lines of code w/ out chaining.
For example from this answer
Snippet 1
var a1 = getPromiseForAjaxResult(ressource1url);
a1.then(function(res) {
append(res);
return a2;
});
How does the function passed to then know when to run.
That is, how is it passed back to the callback code that ajax fires on completion.
Snippet 2
// generic ajax call with configuration information and callback function
ajax(config_info, function() {
// ajax completed, callback is firing.
});
How are these two snippets related?
Guess:
// how to implement this
(function () {
var publik = {};
_private;
publik.then = function(func){
_private = func;
};
publik.getPromise = function(func){
// ??
};
// ??
}())
Fundamentally, a promise is just an object that has a flag saying whether it's been settled, and a list of functions it maintains to notify if/when it is settled. Code can sometimes say more than words, so here's a very basic, not-real-world example purely indended to help communicate the concepts:
// See notes following the code for why this isn't real-world code
function Promise() {
this.settled = false;
this.settledValue = null;
this.callbacks = [];
}
Promise.prototype.then = function(f) {
if (this.settled) {
f(this.settledValue); // See notes 1 and 2
} else {
this.callbacks.push(f);
}
// See note 3 about `then`
// needing a return value
};
Promise.prototype.settle = function(value) { // See notes 4 and 5
var callback;
if (!this.settled) {
this.settled = true;
this.settledValue = value;
while (this.callbacks.length) {
callback = this.callbacks.pop();
callback(this.settledValue); // See notes 1 and 2
}
}
};
So the Promise holds the state, and the functions to call when the promise is settled. The act of settling the promise is usually external to the Promise object itself (although of course, that depends on the actual use, you might combine them — for instance, as with jQuery's ajax [jqXHR] objects).
Again, the above is purely conceptual and missing several important things that must be present in any real-world promises implementation for it to be useful:
then and settle should always call the callback asynchronously, even if the promise is already settled. then should because otherwise the caller has no idea whether the callback will be async. settle should because the callbacks shouldn't run until after settle has returned. (ES2015's promises do both of these things. jQuery's Deferred doesn't.)
then and settle should ensure that failure in the callback (e.g., an exception) is not propagated directly to the code calling then or settle. This is partially related to #1 above, and more so to #3 below.
then should return a new promise based on the result of calling the callback (then, or later). This is fairly fundamental to composing promise-ified operations, but would have complicated the above markedly. Any reasonable promises implementation does.
We need different types of "settle" operation: "resolve" (the underlying action succeeded) and "reject" (it failed). Some use cases might have more states, but resolved and rejected are the basic two. (ES2015's promises have resolve and reject.)
We might make settle (or the separate resolve and reject) private in some way, so that only the creator of the promise can settle it. (ES2015 promises — and several others — do this by having the Promise constructor accept a callback that receives resolve and reject as parameter values, so only code in that callback can resolve or reject [unless code in the callback makes them public in some way].)
Etc., etc.
Can someone implement the most basic promise in a few lines?
Here it is:
function Promise(exec) {
// takes a function as an argument that gets the fullfiller
var callbacks = [], result;
exec(function fulfill() {
if (result) return;
result = arguments;
for (let c;c=callbacks.shift();)
c.apply(null, arguments);
});
this.addCallback = function(c) {
if (result)
c.apply(null, result)
else
callbacks.push(c);
}
}
Additional then with chaining (which you will need for the answer):
Promise.prototype.then = function(fn) {
return new Promise(fulfill => {
this.addCallback((...args) => {
const result = fn(...args);
if (result instanceof Promise)
result.addCallback(fulfill);
else
fulfill(result);
});
});
};
How are these two snippets related?
ajax is called from the getPromiseForAjaxResult function:
function getPromiseForAjaxResult(ressource) {
return new Promise(function(callback) {
ajax({url:ressource}, callback);
});
}
I've implement one in ES7. With chaining, it's 70 lines, if that counts as few. I think State Machine is the right paradigm for implementing promises. Resulting code is more understandable than lots of ifs IMHO. Described fully in this article.
Here's the code:
const states = {
pending: 'Pending',
resolved: 'Resolved',
rejected: 'Rejected'
};
class Nancy {
constructor(executor) {
const tryCall = callback => Nancy.try(() => callback(this.value));
const laterCalls = [];
const callLater = getMember => callback => new Nancy(resolve => laterCalls.push(() => resolve(getMember()(callback))));
const members = {
[states.resolved]: {
state: states.resolved,
then: tryCall,
catch: _ => this
},
[states.rejected]: {
state: states.rejected,
then: _ => this,
catch: tryCall
},
[states.pending]: {
state: states.pending,
then: callLater(() => this.then),
catch: callLater(() => this.catch)
}
};
const changeState = state => Object.assign(this, members[state]);
const apply = (value, state) => {
if (this.state === states.pending) {
this.value = value;
changeState(state);
for (const laterCall of laterCalls) {
laterCall();
}
}
};
const getCallback = state => value => {
if (value instanceof Nancy && state === states.resolved) {
value.then(value => apply(value, states.resolved));
value.catch(value => apply(value, states.rejected));
} else {
apply(value, state);
}
};
const resolve = getCallback(states.resolved);
const reject = getCallback(states.rejected);
changeState(states.pending);
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
static resolve(value) {
return new Nancy(resolve => resolve(value));
}
static reject(value) {
return new Nancy((_, reject) => reject(value));
}
static try(callback) {
return new Nancy(resolve => resolve(callback()));
}
}
Here's a light-weight promise implementation, called 'sequence', which I use in my day-to-day work:
(function() {
sequence = (function() {
var chained = [];
var value;
var error;
var chain = function(func) {
chained.push(func);
return this;
};
var execute = function(index) {
var callback;
index = typeof index === "number" ? index : 0;
if ( index >= chained.length ) {
chained = [];
return true;
}
callback = chained[index];
callback({
resolve: function(_value) {
value = _value;
execute(++index);
},
reject: function(_error) {
error = _error;
execute(++index);
},
response: {
value: value,
error: error
}
});
};
return {
chain: chain,
execute: execute
};
})();
})();
Once initialized, you can use sequence in the following way:
sequence()
.chain(function(seq) {
setTimeout(function() {
console.log("func A");
seq.resolve();
}, 2000);
})
.chain(function(seq) {
setTimeout(function() {
console.log("func B");
}, 1000)
})
.execute()
To enable the actual chaining, you need to call the resolve() function of the seq object, which your callbacks must use as an argument.
Sequence exposes two public methods:
chain - this method simply pushes your callbacks to a private array
execute - this method uses recursion to enable the proper sequential execution of your callbacks. It basically executes your callbacks in the order you've chained them by passing the seq object to each of them. Once the current callback is resolved/rejected, the next callback is executed.
The 'execute' method is where the magic happens. It passes the 'seq' object to all of your callbacks. So when you call seq.resolve() or seq.reject() you'll actually call the next chained callback.
Please, note that this implementation stores a response from only the previously executed callback.
For more examples and documentation, please refer to:
https://github.com/nevendyulgerov/sequence
Here is a simple Promise implementation that works for me.
function Promise(callback) {
this._pending = [];
this.PENDING = "pending";
this.RESOLVED = "resolved";
this.REJECTED = "rejected";
this.PromiseState = this.PENDING;
this._catch = function (error) {
console.error(error);
};
setTimeout(function () {
try {
callback.call(this, this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}.bind(this), 0)
};
Promise.prototype.resolve = function (object) {
if (this.PromiseState !== this.PENDING) return;
while (this._pending.length > 0) {
var callbacks = this._pending.shift();
try {
var resolve = callbacks.resolve;
if (resolve instanceof Promise) {
resolve._pending = resolve._pending.concat(this._pending);
resolve._catch = this._catch;
resolve.resolve(object);
return resolve;
}
object = resolve.call(this, object);
if (object instanceof Promise) {
object._pending = object._pending.concat(this._pending);
object._catch = this._catch;
return object;
}
} catch (error) {
(callbacks.reject || this._catch).call(this, error);
return;
}
}
this.PromiseState = this.RESOLVED;
return object;
};
Promise.prototype.reject = function (error) {
if (this.PromiseState !== this.PENDING) return;
this.PromiseState = this.REJECTED;
try {
this._catch(error);
} catch (e) {
console.error(error, e);
}
};
Promise.prototype.then = function (onFulfilled, onRejected) {
onFulfilled = onFulfilled || function (result) {
return result;
};
this._catch = onRejected || this._catch;
this._pending.push({resolve: onFulfilled, reject: onRejected});
return this;
};
Promise.prototype.catch = function (onRejected) {
// var onFulfilled = function (result) {
// return result;
// };
this._catch = onRejected || this._catch;
// this._pending.push({resolve: onFulfilled, reject: onRejected});
return this;
};
Promise.all = function (array) {
return new Promise(function () {
var self = this;
var counter = 0;
var finishResult = [];
function success(item, index) {
counter++;
finishResult[index] = item;
if (counter >= array.length) {
self.resolve(finishResult);
}
}
for(var i in array) {
var item = array[i];
if (item instanceof Promise) {
item.then(function (result) {
success(result,this);
}.bind(i), function (error) {
array.map(function (item) {
item.PromiseState = Promise.REJECTED
});
self._catch(error);
})
} else {
success(item, i);
}
}
});
};
Promise.race = function (array) {
return new Promise(function () {
var self = this;
var counter = 0;
var finishResult = [];
array.map(function (item) {
if (item instanceof Promise) {
item.then(function (result) {
array.map(function (item) {
item.PromiseState = Promise.REJECTED
});
self.resolve(result);
}, function (error) {
array.map(function (item) {
item.PromiseState = Promise.REJECTED
});
self._catch(error);
})
} else {
array.map(function (item) {
item.PromiseState = Promise.REJECTED
});
self.resolve(item);
}
})
});
};
Promise.resolve = function (value) {
return new Promise(function (resolve, reject) {
try {
resolve(value);
} catch (error) {
reject(error);
}
});
};
Promise.reject = function (error) {
return new Promise(function (resolve, reject) {
reject(error);
});
}
Discussing here.
Fiddle: here.
here is the absolute minimum of a promise architecture
function Promise(F) {
var gotoNext = false;
var stack = [];
var args = [];
var isFunction = function(f) {
return f && {}.toString.call(f) === '[object Function]';
};
var getArguments = function(self, _args) {
var SLICE = Array.prototype.slice;
_args = SLICE.call(_args);
_args.push(self);
return _args;
};
var callNext = function() {
var method = stack.shift();
gotoNext = false;
if (isFunction(method)) method.apply(null, args);
};
var resolve = [(function loop() {
if (stack.length) setTimeout(loop, 0);
if (gotoNext) callNext();
})];
this.return = function() {
gotoNext = true;
args = getArguments(this, arguments);
if(resolve.length) resolve.shift()();
return this;
};
this.then = function(fn) {
if (isFunction(fn)) stack.push(fn);
return this;
};
return this.then(F).return();
}
// --- below is a working implementation --- //
var bar = function(p) {
setTimeout(function() {
console.log("1");
p.return(2);
}, 1000);
};
var foo = function(num, p) {
setTimeout(function() {
console.log(num);
p.return(++num);
}, 1000);
};
new Promise(bar)
.then(foo)
.then(foo)
.then(foo);

Categories