递归函数的回调

Callback on recursive function

以下代码用于从我们的 Web 应用程序中获取 .zip 文件。该文件是通过安全关闭另一个应用程序然后压缩生成的,最后将其发送以供下载。

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

这按预期工作。但是,我需要从另一个脚本中使用它。所以上面的代码 returns 一个 id 下载的文件,然后从另一个脚本我可以提取 .zip 并将提取的文件放入具有相同 id 的目录中。然后可以下载这些文件。

EDIT 本质上我需要执行这个脚本,在下载时提取内容,然后在前两个步骤完成时用 res.render() 加载 UI完全的。这需要使用 id 来完成,这样两个用户就不会创建冲突的文件。

您可以使用异步实用程序库,例如 async

您要查找的模式似乎是 waterfall 模式。 这将允许您将所需的数据从一个任务传递到另一个任务。

function requestFile(cb){
    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 ..');
           cb(null, body);
       }
       else{ 
           process.stdout.write('\rAwaiting file ..');
           setTimeout(requestFile, 30 * 1000)
       }
   });
}


function saveFile(body, cb){
    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');
        cb(null, id);
    })
}

function render(id, cb) {
   //do what ever you need with your id
   cb();
}   

async.waterfall([
    requestFile,
    saveFile,
    render
], function(err){

});

顺便说一句,我建议您将数据从服务器直接流式传输到磁盘,而不是将其全部收集到缓冲区中然后保存。

您可以在请求对象上创建 data 个侦听器,然后将它们直接流式传输到磁盘,甚至只使用 request.pipe(file)

示例:

function streamFile(){

    var id = uuid.v4()
        , file = path.resolve(__dirname, '../../' + id + '.zip');
    var stream = fs.createWriteStream(file);
    stream.on('error', function(err){
        throw err;
    }).on('close', function(){
        process.stdout.write('\rFile downloaded ' + id + '.zip')
    });

    request({
        method: 'GET',
        uri: 'some_url',
        headers: {
           'User-Agent': 'Scripted-Download'
        },
        encoding: null,
        jar: true
    }).on('error', function(err) {
        throw(err)
    }).on('response', function(res){
        if (res.headers['content-type'] === 'application/zip;charset=utf-8') {
            process.stdout.write('\rDownloading file ..');
            cb(null, body);
        }
        else{
            process.stdout.write('\rAwaiting file ..');
            res.destroy();
            setTimeout(streamFile, 30 * 1000)
        }
    }).pipe(stream);
}

如评论中所述,promises 应该使这变得容易。首先承诺您需要的异步功能:

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

然后使用它们。

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