I'm trying to reverse engineer async's waterfall function to better understand how it works.
While I've come up with the following code which works mostly, it has one strange behavior I cannot explain: Arguments passed to the next function seem to get lost somewhere, being replaced by the load event in browsers and the require function in node.
My code:
const waterfall = (callbacks, callback) => {
callback = callback || function() {
};
let counter = 0,
errors = [],
results = [],
nextExecutor = (err, res) => {
let argsArray = Array.from(arguments),
error = (argsArray.length >= 2 && !!argsArray[ 0 ]
? argsArray[ 0 ]
: false
),
result = (error
? argsArray[ 1 ]
: argsArray[ 0 ]
),
currentIteration = counter++,
currentNextExecutor = (counter === callbacks.length
? () => {
}
: nextExecutor
);
if (error !== false) {
errors.push(error);
}
results.push(result);
callbacks[ currentIteration ].call(
{},
currentNextExecutor,
result
);
if (counter === callbacks.length) {
callback.call(
{},
(errors.length > 0
? errors[ 0 ]
: null
),
results
);
}
};
callbacks[ counter++ ](nextExecutor);
};
waterfall([
function(next) {
console.log('hi! this is first.');
setTimeout(() => {
next(null, 10);
}, 200);
},
function(next, x) {
console.log('hi! this is second: ' + x);
setTimeout(() => {
next(null, x);
}, 200);
},
function(next, x) {
console.log('hi! this is third: ' + x);
x++;
setTimeout(() => {
next(null, x);
}, 200);
},
function(next, x) {
console.log('hi! this is last.');
setTimeout(() => {
next(null, x);
}, 200);
}
], (error, results) => {
console.log('Error: ' + JSON.stringify(error));
console.log('Results: ' + JSON.stringify(results));
});
Expected output:
hi! this is first.
hi! this is second: 10
hi! this is third: 10
hi! this is last.
Error: null
Results: [ 10, 10, 11, 11]
Actual output:
hi! this is first.
hi! this is second: function require(path) { ... }
hi! this is third: function require(path) { ... }
hi! this is last.
Error: {}
Results: [null,null,null]
I don't understand where things go wrong - the arguments
object has no reference of the passed value.
Notice: This is not intended to mimic async source code, because I haven't looked at it yet. I wanted to solve the problem by myself for learning purposes.
Since arrow functions don't bind their own arguments
variable, I was using the parent scope arguments
. Which, incidentially, was the load
event in browsers or require
in node.
Solved the problem - interestingly enough, using arrow functions does not work as intended, though I don't know why. Swapping the declaration for nextExecutor
to a standard function (and refactoring the argument handling a bit) seems to fix the issue.
Thus, the nextExecutor
code looks like this:
nextExecutor = function(error, result) {
let currentIteration = counter++,
currentNextExecutor = (counter === callbacks.length
? function() {}
: nextExecutor
);
if (!!error) {
errors.push(error);
}
results.push(result);
callbacks[ currentIteration ].call(
{},
currentNextExecutor,
result
);
if (counter === callbacks.length) {
callback.call(
{},
(errors.length > 0
? errors[ 0 ]
: null
),
results
);
}
};
callbacks[ counter++ ](nextExecutor);
};
Well your problem was here
result = (error
? argsArray[ 1 ]
: argsArray[ 0 ]
),
Since error is always false, result would be args[0] which is always null ?
argsArray[indexes] are inverted in other words ...
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.