0

I am trying to save the file and details of it in the database.

Waterfall function with two calls but the second function is not waiting for the first function to finish.

Even without using waterfall eachseries doesn't work as expected. It doesn't wait for the record to be created and hence uniqueness error occurs due to same id. What am I doing wrong here and how can I fix it?

Thanks!

       async.eachSeries(uploadedPhotos, async function (uploadedFile, callback) {

          async.waterfall([
            async function() {
              var lastUser = await TblUserFiles.find({}).sort('id DESC').limit(1);
              // fileID remains undefined if i remove async-await from the  function 
              var fileID = lastUser[0].id;
              fileID += 1;
              cbb(null, fileID, uploadedFile);
            },
            async function(file_id, uploadedFile, cbb) {
              sails.log("save file id is " + file_id);
              sails.log("savee file id is " + uploadedFile['filename']);
              var today = moment(new Date()).format('YYYY-MM-DD HH:mm:ss');
              await TblUserFiles.findOrCreate({ customer_no: req.session.userId, file_type: 'profile_image' }, {
                id: fileID,
                customer_no: req.session.userId,
                file_name: uploadedFile['filename'],
                file_type: 'profile_image',
                is_approved: 'No',
                approved_by: 0,
                approved_on: today,
                approved_from: ip,
                uploaded_date: today,
                modified_date: today
              }).exec(async (err, user, wasCreated) => {
                if (err) { return res.serverError(err); }

                if (wasCreated) {
                  // created a new user
                  sails.log("new file was uploaded...")
                  return cbb(err, "done");

                  // return res.send("sent");
                }
                else {
                  // found existing user
                  var user = await TblUserFiles.create({
                    id: fileID,
                    customer_no: req.session.userId,
                    file_name: uploadedFile["filename"],
                    file_type: "image",
                    is_approved: "No",
                    approved_by: 0,
                    approved_on: today,
                    approved_from: ip,
                    uploaded_date: today,
                    modified_date: today
                  }).intercept(err => {
                    // Return a modified error here (or a special exit signal)
                    // and .create() will throw that instead
                    err.message = "Uh oh: " + err.message;
                    return err;
                  }).fetch();
                  if (user) {
                    sails.log("found existing files..")
                    return cbb(err, "done");
                  }
                  sails.log("this should not be called");
                }
              });
            }
          ], (err, success) => {
            if (err) sails.log(err);
            return callback(err, 'done')
          });



        }, function (err) {
          // if any of the saves produced an error, err would equal that error
          if (err) {
            sails.log(err);
          } else {
            return res.json({
              message: uploadedPhotos.length + ' file(s) uploaded successfully!',
              files: uploadedPhotos
            });
          }
        });
6
  • 2
    So Node asycnjs library and the async/await keywords are two very different things. The two do not intermix as you are attempting here. Also doing const async = require('async') as you would have done earlier actually overwrites the usage of the async keyword in further use. If you must use the library ( you really don't need to now ), then instead do const Async = require('async') so at least there is a case difference between the two terms. But even then you cannot mark callback functions as async. Commented Nov 19, 2018 at 8:22
  • 1
    Basically promise.then(res => newpromise(res)).then(...) is basically what a "waterfall" is. Or var res1 = await promise; var2 = await promise2(res1) is the async/await version of waterfall. So the two patterns really are mutually exclusive. Commented Nov 19, 2018 at 8:25
  • @NeilLunn I haven't overridden async. I am using sails.js framework here. Commented Nov 19, 2018 at 8:34
  • 1
    Yes you have overridden it. async.waterfall and async function() both use the lowercase async, which I'm pointing out is a "keyword". I'm also outright "showing you" why you don't need this, and that you simply appear to be copying an approach from an older source you found online way back when usage of "async js" was a popular thing. As in, a couple of years ago now. It's pretty much redundant if your nodejs supports async/await, or even promises in general. Commented Nov 19, 2018 at 8:38
  • @NeilLunn Okay. I understand that I don't need waterfall but that still doesn't allow me to loop through the files using eachseries as it shows uniqueness error. Commented Nov 19, 2018 at 9:36

1 Answer 1

2

Updated answer:

I rewrite the code. I forgot that async.js handles promise in different way. Basically, you don't use callback(). Use return instead. If an error occurs, use throw new Error(message). See here for more information. Read topic: Using ES2017 async functions.

async.eachSeries(uploadedPhotos, async uploadedFile => {
  var lastUser = await TblUserFiles.find({}).sort('id DESC').limit(1);
  var fileID = lastUser[0].id;
  fileID += 1;
  sails.log("save file id is " + file_id);
  sails.log("savee file id is " + uploadedFile['filename']);
  var today = moment(new Date()).format('YYYY-MM-DD HH:mm:ss');
  await TblUserFiles.findOrCreate(
    { customer_no: req.session.userId, file_type: 'profile_image' },
    {
      id: fileID,
      customer_no: req.session.userId,
      file_name: uploadedFile['filename'],
      file_type: 'profile_image',
      is_approved: 'No',
      approved_by: 0,
      approved_on: today,
      approved_from: ip,
      uploaded_date: today,
      modified_date: today
    }
  ).exec(async (err, user, wasCreated) => {
    if (err) throw new Error(err);
    if (wasCreated) {
      sails.log("new file was uploaded...");
      return;
    } else {
      await TblUserFiles.create({
        id: fileID,
        customer_no: req.session.userId,
        file_name: uploadedFile["filename"],
        file_type: "image",
        is_approved: "No",
        approved_by: 0,
        approved_on: today,
        approved_from: ip,
        uploaded_date: today,
        modified_date: today
      })
      .intercept(err => {
        throw new Error("Uh oh: " + err.message);
      }).fetch();
      if (user) {
        sails.log("found existing files..");
        return;
      } else {
        sails.log("this should not be called");
        return;
      }
    }
  });
}, err => {
  // Don't call res.serverError() or res.json() inside the async loop!
  if (err) {
    sails.log(err);
    res.serverError(err);
  }
  else {
    res.json({
      message: uploadedPhotos.length + ' file(s) uploaded successfully!',
      files: uploadedPhotos
    });
  }
});

I think the first problem here is that the you forget to give the first task a callback function(cbb in this case).

async.waterfall([
            async function(cbb) {
              var lastUser = await TblUserFiles.find({}).sort('id DESC').limit(1);
              // fileID remains undefined if i remove async-await from the  function 
              var fileID = lastUser[0].id;
              fileID += 1;
              cbb(null, fileID, uploadedFile);
            },
            async function(file_id, uploadedFile, cbb) {
...

Secondly, you shouldn't return a callback. Callback is a function not a promise. Just use them normally.


By the way, const async = require('async'); will NOT override async keyword. Compiler can tell the difference between them. This is proved by the following script example:

const async = require('async');

let runPromise = (name, timer, success = true) => {
  console.log(`${name} starts.`);
  return new Promise((resolve, reject) => {
    if (success) {
      setTimeout(function () {
        resolve(`${name} finished after ${timer / 1000} seconds(resolved).`);
      }, timer);
    } else {
      reject(`${name} failed(rejected).`);
    }
  });
};

async function asyncFunction() {
  let txt = await runPromise('A', 1000);
  console.log(txt);
}

asyncFunction();
Sign up to request clarification or add additional context in comments.

1 Comment

I've added cbb and removed return from callbacks. But, still getting TypeError: cbb is not a function`

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.