4

I have 3 layer callbacks like this :

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

In this case, there are only 3 file uploads. In another case, I have more than 10 file uploads, and it makes 10 layer callbacks. I know it because of JavaScript asynchronous.

Is there any way, with this case, to make a beautiful code? This is because when it 10 layer callbacks, the code looks horizontally weird.

Thanks

7
  • I would use Promises and async/await to straighten this up. Commented Feb 22, 2019 at 12:15
  • 2
    Check out promises. You might want to have a look here for a start: developers.google.com/web/fundamentals/primers/promises They are made for cases like the one you're describing. Commented Feb 22, 2019 at 12:16
  • I use npm package, it only have callback Commented Feb 22, 2019 at 12:16
  • Do you need them in a row? I mean is it a workflow that they are one after each other and only if error? Commented Feb 22, 2019 at 12:18
  • 1
    I use npm package - what package? it only have callback - why is this a problem? Promisify it or use a package that supports a promise (this depends on the package, which you didn't list). Commented Feb 22, 2019 at 12:21

4 Answers 4

6

You can use the following code to make you code look better and avoid callback hell

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);
    }
});
Sign up to request clarification or add additional context in comments.

1 Comment

note that async await is only supported in "late node-7 and above"
3

You can make the functions return a Promise

I advice to make one function because you do the same thing 3 times. In this case I called the function 'save' but you can call it what ever you want. The first parameter is the file end the second the output filename.

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

Comments

0

What version of Node you using? If async/await is available that cleans it up a bunch.

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

I'm also assuming here that your trainInput, trainOutput, testOutput weren't all meant to be req.files.trainInput.

Just be careful since the synchronous nature of the await calls are thread blocking. If that writer function takes ages you could also looking at putting those calls onto a worker thread. Won't really matter if your requests to that server endpoint are fast and non-frequent.

1 Comment

Oh I took forever to answer haha I see there already is another with async/await.
0

You can add RXJS to your project and use Observables.forkJoin()

Solution with Observables(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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.