douglascrockford / parseq Goto Github PK
View Code? Open in Web Editor NEWBetter living thru immediacy!
Better living thru immediacy!
Thanks for sharing this library.
Can you explain your rationale why you chose positional arguments?
In the past for various other uses you have advocated for a spec
object.
For example in parseq parallel
parseq.parallel(
required_array,
optional_array,
time_limit,
time_option,
throttle
)
If I wanted to only throttle a list of requestors my call looks like this.
parseq.parallel([requestors], [], undefined, undefined, 10)
I am forced to specify some unneeded values, and in the future if more functionality is required it will get even more complicated.
If a spec object was used instead features could be added without breakages.
And if a feature should be deprecated in the future it would be possible for your library to simply ignore it.
I would advocate for only required arguments being positional.
parseq.parallel(
required_array,
{spec/opts/whatever...}
)
thoughts?
In some extreme cases, the value array produced by parseq.parallel() is of a different length to the requestor array. I experience this on Node (v15), Chrome (v86) and yes, even on Safari (v14).
/*jslint node, browser */
import parseq from "./parseq.js";
parseq.parallel(
new Array(20000).fill(
(callback) => callback(true)
)
)(function (value) {
console.log(value.length);
});
$ node parallel_fail.js
15170
This may seem far-fetched, but I came across it and it had me stumped. Throttling seems to have no effect.
I'm not sure if it's really a bug or I have misunderstood some parseq basic concepts.
It is stated at page 20.9 that requestors cannot throw exceptions but they're supposed to pass their failures as a second parameter (first is undefined) when invoking the callback, but I'm not sure if the same applies to callbacks. Let me show you some examples:
Example 1
parseq.sequence([
function requestor(callback) {
callback(1);
}
])(function (value, reason) {
console.count("pass");
if (value === undefined) {
console.log("There is a failure");
return console.log(reason);
}
throw new Error("Booom!");
});
Output:
pass: 1
pass: 2
There is a failure
Error: Booom!
Are callbacks supposed to be called more than wunce?
Example 2
//taken from page 20.5
function requestorize(unary) {
return function requestor(callback, value) {
try {
return callback(unary(value));
} catch (exception) {
return callback(undefined, exception);
}
}
parseq.sequence([
requestorize(function () {
return 1;
})
])(function (value, reason) {
console.count("pass");
if (value === undefined) {
console.log("There is a failure");
return console.log(reason);
}
throw new Error("Booom!");
});
Output:
pass: 1
In this case, the program silently fails. Which is not good.
But I can replicate the example 1 behavior with the following requestorize factory:
function requestorize(unary) {
return function requestor(callback, value) {
let return_value;
try {
return_value = unary(value);
} catch (exception) {
return callback(undefined, exception);
}
callback(return_value);
};
}
...but I think the same applies (in more complex contexts) to the try catch block within the start_requestor function on line 141 in parseq.js. EDITED: Like in the following example:
Example 3
parseq.sequence([
parseq.sequence([
function requestor(callback) {
callback(1);
}
])
])(function (value, reason) {
console.count("pass");
if (value === undefined) {
console.log("There is a failure");
return console.log(reason);
}
throw new Error("Booom!");
});
Output:
pass: 1
And of course, if I execute the requestors without any parseq factory, I have another different behavior. But probably requestors shouldn't be executed "stand-alone".
In conclusion, I think there is ambiguity in terms of how the callbacks should manage failures. What is your opinion, mr. Crockford? I'm a bit puzzled...
Why there is parallel_object
and no race_object
!?
Often I find it necessary to generate a requestor array using the result of a prior requestor. I love that parseq is able to do this, but my approach is hard to follow and thus error prone.
function download_file(url) {
return download_file_requestor(callback) {
...
}
}
parseq.sequence([
fetch_urls(),
download_files_requestor(callback, urls) {
try {
return parseq.parallel(
urls.map(download_file)
)(callback); // It's easy to miss this invocation!
} catch (exception) {
return callback(undefined, exception);
}
}
]);
It occurred to me that parseq could accept functions in place of requestor arrays. These would take the value and return a requestor array.
parseq.sequence([
fetch_urls(),
parseq.parallel(
function make_required_requestors(urls) {
return urls.map(download_file);
}
)
]);
Is this a good idea?
During development, I often want to pass console.log
as the callback function to a requestor. However, parseq requires that the callback.length
property is exactly 2, so it rejects console.log
because console.log.length
is 0.
I also feel it would make sense for parseq to accept unary functions as callbacks. Since the success or failure of a requestor is encoded in the value
, the reason can safely be ignored without changing the behaviour of the program.
Would you please consider removing the arity check for callbacks?
I want to promote Distributed Eventual programming throughout the Javascript Ecosystem, and I'm building an npm package that uses requestors. I was wondering if I could create an npm package from parseq, to make it easy to integrate into a project.
You can find the would-be source code of the package here.
Dear Mr. Crockford,
woud you mind opening a discussion section, like the one in jslint?
https://github.com/jslint-org/jslint/discussions
to share suggestions, ideas, etc. between parseq users
Kind Regards
Is the use of word "wun" intentional? It appears many times in https://github.com/douglascrockford/parseq/blob/master/parseq.js and I can't tell for sure if it is a typo or not.
The documentation states that A requestor should not throw an exception. It should communicate all failures through its callback.
which is understandable, however the code will catch exceptions and use them as the reason
for the failure. This also makes sense since async code should not be allowed to throw in production, but I would disagree that the same is true for development. While it is true that a requestor function should not throw, that is not something that is always under our control since a mistake could easily cause the code to throw especially during development. Perhaps it would make sense to throw the reason after parseq has finished its task, so that we can crash the app. Maybe it would make sense to add an attribute to the reason that was caused by an exception so that we have the option to handle it differently than a normal failure. For example:
} catch (exception) {
exception.thrown = true; // set attribute so that we can detect it later
action(undefined, exception, number);
number = undefined;
start_requestor(value);
}
I think there is an old link from crockford.com linking to removed RQ library. Maybe you can consider changing the link and RQ page..
Cheers!
Using the provided demo application, trigger fallback in the following order:
Fall F0: failure
Fall F1: failure
Fall F2: success
A "Uncaught TypeError: callback is not a function" will be exception will happen. This only happen when last requestor in the array is the only wun that succeed while the rest fail.
Same exception also occur for race, when last requestor succeed while the rest fail
Race B0: failure
Race B1: failure
Race B2: success
or
Race B0: failure
Race B2: failure
Race B1: success
Additionally, using race or fallback factory with only a single success requestor also cause the exception.
The documentation does not specify that the requestor arrays cannot be empty, yet when they are parseq fails with parseq.parallel: Missing requestor array.
.
It would be less surprising if parseq allowed empty arrays, for situations where the requestor arrays are being generated on the fly, for instance:
parseq.sequence([
getJobs(),
function(callback, jobs) {
return parseq.parallel(
// one requestor per job
jobs.map(function(job) {
return processJob(job); // returns a requestor
});
})(callback);
},
])
The above code worked flawlessly until an edge case occurred and I received an empty jobs
array, which was not an error.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.