1

I want to upload some files, add them to a database and return the ids of the new objects.

fn: async function (inputs) {

  let docIds = []
  let settings = {...}

  await inputs.filesToUpload.upload(settings, async (err, files) => {

    if (err)
        throw {'invalid': 'The provided data is invalid.'}

    for (let i = 0; i < files.length; i += 1) {
        let newDocument = await Document.create({
          name: file.filename
        }).fetch()
        docIds.push(newDocument.id)
    }

  })

  return {
    ids: docIds
  }
})

Unfortunately, the controller doesn't wait for the objects to be created in the database and returns {ids: []} immediately, only then the documents are uploaded and objects created. I have tried using passing the ids as a callback and promise but the controller always executes return without waiting for the results.

silencedogood
  • 3,209
  • 1
  • 11
  • 36
Money_Badger
  • 537
  • 1
  • 5
  • 24
  • I could give an answer here, but this old post gives a WAY better explanation/solution than I could give, check it out: https://stackoverflow.com/a/43766002/9040866 – silencedogood Jul 29 '19 at 15:49

1 Answers1

3

inputs.filesToUpload.upload is making a callback which is always going to be asynchronous.

async keyword before a function with callback can not make it wait.

async works only if the function is returning a promise

check following code where I have extracted the upload flow in a separate function that returns a promise

you can then await on this promise and get generated ids..

async function test(inputs) {
    const docIds = await upload(inputs, {});
    return { ids: docIds };
}

function upload(inputs, settings) {
    return new Promise((resolve, reject) => {
        const ids = [];
        inputs.filesToUpload.upload(settings, async (err, files) => {
            if (err) {
                return reject({ 'invalid': 'The provided data is invalid.' });
            }
            for (let i = 0; i < files.length; i += 1) {
                let newDocument = await Document.create({ name: file.filename }).fetch();
                ids.push(newDocument.id);
            }
            resolve(ids);
        });
    });
}

Please note that the above function is just clarifying the use of promises.

It can be implemented in various ways specifically if we want to optimise it.

EDIT

As an example, Promise.all can be used to optimise the upload if order is not a concern, something like like this -

function upload(inputs, settings) {
    return new Promise((resolve, reject) => {
        const ids = [];
        inputs.filesToUpload.upload(settings, async (err, files) => {
            if (err) {
                return reject({ 'invalid': 'The provided data is invalid.' });
            }
            const newDocuments = await Promise.all(files.map(file => 
                Document.create({ name: file.filename }).fetch())
            );
            resolve(newDocuments.map(newDocument => newDocument.id));
        });
    });
}

I hope this helps.

planet_hunter
  • 3,866
  • 1
  • 26
  • 39
  • 1
    Just don't forget to return reject method. You don't want to continue after you got error. – hlozancic Jul 30 '19 at 06:14
  • Indeed! Thanks @hlozancic for the correction. I should ideally use `try-catch` to make sure I'm catching uncaught exceptions, specifically from await calls. I just concentrated on the main problem and ended up writing a pseudo code which definitely has got a lot of room for improvements. I have corrected the code as per your suggestion anyway, thanks :) – planet_hunter Jul 30 '19 at 07:25