Javascript如何更好地编码嵌套回调?

时间:2019-02-22 12:13:44

标签: javascript node.js

我有3层这样的回调:

    app.post('/', (req, res) => {
        var filename = `outputs/${Date.now()}_output.json`;
        let trainInput = req.files.trainInput;
        let trainOutput = req.files.trainInput;
        let testInput = req.files.trainInput;

        //first
        trainInput.mv(`inputs/${req.body.caseName}/train_input.csv`, function (err) {
            if (err) return res.status(500).send(err);
            //second
            trainOutput.mv(`inputs/${req.body.caseName}/train_output.csv`, function (err) {
                if (err) return res.status(500).send(err);
                //third
                testInput.mv(`inputs/${req.body.caseName}/test_input.csv`, function (err) {
                    if (err) return res.status(500).send(err);

                    res.send('success');
                });
            });
        });   
    });

在这种情况下,仅上传3个文件。在另一种情况下,我上传的文件超过10个,它进行了10层回调。我知道这是因为JavaScript是异步的。

在这种情况下,有什么办法可以编写漂亮的代码?这是因为当它进行10层回调时,代码在水平上看起来很奇怪。

谢谢

4 个答案:

答案 0 :(得分:6)

您可以使用以下代码使您的代码看起来更好并避免回调地狱

app.post('/', async (req, res) => {
    var filename = `outputs/${Date.now()}_output.json`;
    let trainInput = req.files.trainInput;
    let trainOutput = req.files.trainInput;
    let testInput = req.files.trainInput;
    try {
        var result1 = await trainInput.mv(`inputs/${req.body.caseName}/train_input.csv`);
        var result2 = await trainInput.mv(`inputs/${req.body.caseName}/train_output.csv`);
        var result2 = await testInput.mv(`inputs/${req.body.caseName}/test_input.csv`);
        res.send('success');
    }
    catch (error) {
        res.status(500).send(error);
    }
});

答案 1 :(得分:3)

您可以使函数返回Promise

我建议您执行一项功能,因为您执行相同的操作3次。在这种情况下,我将函数称为“保存”,但您可以随意调用它。第一个参数是文件末尾,第二个参数是输出文件名。

function save(file, output) = return new Promise((resolve, reject) => {
  file.mv(`inputs/${req.body.caseName}/${output}`, err => 
  if (err) return reject(err)
  resolve()
})

Promise.all([
    save(req.files.trainInput, 'train_input.csv'),
    save(req.files.trainInput, 'train_output.csv'),
    save(req.files.trainInput, 'test_input.csv')
])
.then(_ => res.send(200))
.catch(err => res.send(400);

答案 2 :(得分:0)

您使用的是哪个版本的Node?如果异步/等待可用,则将其清理一堆。

const moveCsv = (file, dest) => {
    return new Promise((resolve, reject) => {
        //third
        file.mv(dest, function (err) {
            if (err) reject(err);
            resolve();
        });
    })
}

app.post('/', async(req, res) => {
    try {
        var filename = `outputs/${Date.now()}_output.json`;

        const {
            trainInput,
            trainOutput,
            testInput
        } = req.files;

        const prefix = `inputs/${req.body.caseName}`;
        await moveCsv(trainInput, `${prefix}/train_input.csv`);
        await moveCsv(trainOutput, `${prefix}/train_output.csv`);
        await moveCsv(testInput, `${prefix}/test_input.csv`);
        res.send('success');
    } catch(err) {
        res.status(500).send(err);
    }
});

我在这里还假设您的trainInputtrainOutputtestOutput并非全部都是req.files.trainInput

请小心,因为await调用的同步性质是线程阻塞。如果那个writer函数需要很长时间,您还可以考虑将这些调用放在工作线程上。您对该服务器端点的请求是否快速且不频繁并不重要。

答案 3 :(得分:0)

您可以将 RXJS 添加到项目中,并使用 Observables.forkJoin()

可观察物的解决方案( assuming that trainInput.mv() returns Observable ):

/* Without a selector */
var source = Rx.Observable.forkJoin(
  trainInput.mv(`inputs/${req.body.caseName}/train_input.csv`),
  trainInput.mv(`inputs/${req.body.caseName}/train_output.csv`),
  trainInput.mv(`inputs/${req.body.caseName}/test_input.csv`)
);

var subscription = source.subscribe(
  function (x) {
    // On success callback
    console.log('Success: %s', x);
  },
  function (err) {
    // Error callback
    console.log('Error');
  },
  function () {
    // Completed - runs always
    console.log('Completed');
  });

// => Success: [result_1, result_2, result_3] or Error
// => Completed