Search code examples
javascriptnode.jsnode-async

Api design for a unsafe async function


I've read that is bad practice to mix synchronous and asynchronous operations in a function (e.g. http://nodejs.org/api/process.html#process_process_nexttick_callback).

Now i want to iterate a array of documents which i want to save in the database. Before the save begins, i need to validate all documents by step. A specific validator can be created later outside from the api.

The validate function can be completly synchronous (simple checking fields if they are valid like doc.sum > 10) but also asynchronous (if i need to get and validate a dependency document).

With the async library (https://github.com/caolan/async) i have something like this:

function validateAll(docs) {
    async.eachSeries(docs, function (doc, callback) {
        var async = false;
        validate(doc, function () {
            if (async) {
                callback();
            } else {
                process.nextTick(function () {
                    callback();
                });
            }
        });

        async = true;
    });
}

function validate(doc, callback) {
    if (doc.sum < 10) {
        return callback(new Error("Invalid sum"));
    }

    if (doc.otherDocumentId) {
        validateOtherDocFromDbAsync(doc.otherDocumentId, callback); // collection.find() or something
    } else {
        callback();
    }
}

In this example, i set the iteration callback on the next tick to avoid a "Maximum call stack size exceeded" error if many callbacks are called synchronously.

I have also thought to create validateSync and validateAsync functions to exactly define what they do.

Is this a bad practice? How would you design the api with this example?


Solution

  • Is this a bad practice?

    Yes. http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony

    How would you design the api with this example?

    Instead of passing a callback to validate that handles whether it was called asynchronously or not, you should just make the validate function always asynchronous:

    function validateAll(docs) {
        async.eachSeries(docs, validate);
    }
    
    function validate(doc, callback) {
        if (doc.sum < 10) {
            process.nextTick(function () {
                callback(new Error("Invalid sum"));
            });
        } else if (doc.otherDocumentId) {
            validateOtherDocFromDbAsync(doc.otherDocumentId, callback); // collection.find() or something
        } else {
            process.nextTick(callback);
        }
    }