Search code examples
javascriptnode.jscallbackorder-of-execution

Correct for loop implementation


I've written some code to convert swagger 1 documentation to swagger 2. I point the conversion method to several resources in an array. what I find is that it is not executing correctly and see it jump in the debugger all the way to the end of my array (which is of size 34). How do I ensure it loops through my code correctly?

for(var i = 0; i < resourcesArray.length; i++) {
    Converter.convert({
        from: 'swagger_1',
        to: 'swagger_2',
        source: 'http://example/' + resourcesArray[i]
    }, function (err, converted) {
        console.log(resourcesArray[i]);
        // [Optional] Fill missing fields with dummy values
        converted.fillMissing();

        // [Optional] Validate converted spec
        var fileName = resourcesArray[i] + '.json';
        fs.writeFileSync(fileName, converted.stringify());
    })
}

Solution

  • You've fallen victim to JavaScript scoping rules. Try this:

    resourcesArray.forEach(function (resource) {
        Converter.convert({
            from: 'swagger_1',
            to: 'swagger_2',
            source: 'http://example/' + resource
        }, function (err, converted) {
            console.log(resource);
            // [Optional] Fill missing fields with dummy values
            converted.fillMissing();
    
            // [Optional] Validate converted spec
            var fileName = resource + '.json';
            fs.writeFileSync(fileName, converted.stringify());
        });
    });
    

    The problem was that by the time the asynchronous callback function (err, converted) { ... } occurs, i is equal to resourcesArray.length because the iteration is already complete. That is how JavaScript var declared variables work. Using a forEach loop ensures that the scope always contains the resource you're expecting for that operation.

    Alternatively, if ES6 is okay, then you could change var to let and that would also solve the problem because let-declared variables use lexical scoping, which means that the for loop block will always contain the expected value of i even if it's used inside an asynchronous callback.