Search code examples
javascriptnode.jsrecursionrequestjs

Callback on recursive function


The following code is used to fetch a .zip file from our web application. The file is generated by closing another application safely then zipping, finally sending it for download.

var dl = function() {
    request({
        method: 'GET',
        uri: 'some_url',
        headers: {
            'User-Agent': 'Scripted-Download'
        },
        encoding: null,
        jar: true
    }, function(err, res, body) {
        if (err) throw(err)
        if (res.headers['content-type'] === 'application/zip;charset=utf-8') {
            process.stdout.write('\rDownloading file ..')
            var id = uuid.v4()
            , file = path.resolve(__dirname, '../../' + id + '.zip')
            fs.writeFile(file, body, function(err) {
                if (err) throw(err)
                process.stdout.write('\rFile downloaded ' + id + '.zip')
                process.exit(0)
            })
        } else {
            process.stdout.write('\rAwaiting file ..')
            setTimeout(dl(), 30 * 1000)
        }
    })
}

This works as expected. However, I need to use this from another script. So the above code returns an id of the file downloaded, then from another script I can extract the .zip and place the extracted files into a directory with the same id. These files would then be made available to download.

EDIT Essentially I need to execute this script, extract the contents when it's downloaded then load a UI with res.render() when the previous two steps are complete. This needs to be done with an id so that two users don't create conflicting files.


Solution

  • As mentioned in the comments, promises should make this easy. First promisify the async functionality you need:

    function makeRequest(parameters) {
        return new Promise(function (resolve, reject) {
            request(parameters,  function (err, res, body) {
                if (err) { reject (err); }
                else { resolve({ res: res, body: body }); }
            });
        });
    }
    
    function writeFile(file, body) {
        return new Promise(function (resolve, reject) {
            fs.writeFile(file, body, function(err) {
                if (err) { reject(err); }
                else { resolve(); }
            });
        });
    }
    
    function timeout(duration) {
        return new Promise(function (resolve) {
            setTimeout(resolve, duration);
        });
    }
    

    Then use them.

    var dl = function () {
        return makeRequest({
            method: 'GET',
            uri: 'some_url',
            headers: {
                'User-Agent': 'Scripted-Download'
            },
            encoding: null,
            jar: true
        }).then(function (result) {
            if (result.res.headers['content-type'] === 'application/zip;charset=utf-8') {
                process.stdout.write('\rDownloading file ..')
                var id = uuid.v4()
                , file = path.resolve(__dirname, '../../' + id + '.zip');
    
                return writeFile(file, result.body)
                    .then(function () { return id; });
            } else {
                process.stdout.write('\rAwaiting file ..');
    
                return timeout(30 * 1000).then(dl);
            }
        });
    }
    
    dl().then(function (id) { process.stdout.write('\rid is: ' + id); });