Giter Site home page Giter Site logo

parseq's People

Contributors

douglascrockford avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

parseq's Issues

Positional Arguments

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?

Lost parallel results

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.

Are callbacks supposed to throw exceptions?

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...

race_object

Why there is parallel_object and no race_object!?

Requestor array factories

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?

Non-binary callbacks

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?

May I please create an npm package called crockford-parseq?

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.

Should exceptions in requestors be special?

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);
}

Uncaught exception when only last requestor succeed and the rest fail for Fallback and Race

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.

Empty requestor arrays

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.