2

I am trying to use a Sequelize promise inside of an existing async/await chain. Sequelize returns everything as a promise. I am new to promises and async/await so I am using this as something of a learning experience. Please be extra patient with me.

My logic looks like this:

if row does not exist from previous '.then'

create a new row and extract the new id

else

extract id value from previous '.then'

So now I have a WORKING block of code now (below), from studying many excellent SO examples. I want to use all anonymous functions since IMHO it makes it a little bit easier to read. (yes debatable point, but for now I want to try getting this working with anonymous functions.

It seems odd to have three nested return statements. Is there a way to rewrite this block of code to be a bit cleaner but just using anonymous functions?

yourTable.findAll( {...})
.then( (data) => {
      // more code
})
.then( (data) => {
      // more code
})
.then( (data) => {
     if  ( !Array.isArray(data) || !data.length ) {  
           // if record does NOT exist
         return (async function ()       {
             return await
                 (function ()    {
                     return new Promise( resolve => {
                         myTable.create( { ........ }
                            ).then( (result) => {
                                resolve(result._id);
                        })
                    })
                })();
            })()    
            /* we had to create one, so we return the *NEW* _id value */
    } else {
            /* we found one, so we just return the pre-existing _id value */                                                                                      
        return  data[0]._id    ;
    }
})
.then( (data) => {
    // more code
})
.then( (data) => {

Thank you all.

edwardsmarkf
  • 1,387
  • 2
  • 16
  • 31

2 Answers2

4

Promises are just values which can be awaited but you actually don't strictly need to await them, especially if your surrounding structure isn't using async/await. You could write it like this:

yourTable.findAll({ /* ... */ })
    .then((data) => {
        if (!Array.isArray(data) || !data.length) {
            // We need to create one, return the new ID
            return myTable.create({ /* ... */ })
                .then((result) => result._id)
        } else {
            // We found one, return the existing ID
            return data[0]._id
        }
    })

If you return a Promise from within a .then() it will become part of the parent chain which is probably exactly what you want.

If you really want to use async/await, then I would recommend flipping the logic and doing the larger work after your happy path is accounted for like so:

yourTable.findAll({ /* ... */ })
    .then(async (data) => {
        if (data && data.length) return data[0]._id

        const result = await myTable.create({ /* ... */ })

        return result._id
    })
matpie
  • 17,033
  • 9
  • 61
  • 82
  • 1
    where the **** were you yesterday? you would have saved me several hours of useless research. but seriously, thank you very much. my logic i am following is a bit odd since i have to read one of the table twice, however your first suggestion worked perfectly and made the code far more readable. i am surprised a situation like this does not occur more frequently. you should publish your excellent suggestion somewhere, and not just here. – edwardsmarkf Aug 27 '19 at 01:34
2

Drop all the unnecessary IIFE, IIAFE, the return await and the Promise constructor antipattern:

if (!Array.isArray(data) || !data.length) {
    // if record does NOT exist
    const result = await myTable.create({ ........ });
    return result._id; // we had to create one, so we return the *NEW* _id value 
} else {
    return data[0]._id; // we found one, so we just return the pre-existing _id value
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • that was the first thing i tried, and i got this back: "SyntaxError: await is only valid in async function" – edwardsmarkf Aug 27 '19 at 00:14
  • 1
    Sure, you would need to use an `async (data) => {` callback to your `then` function of course. But really you shouldn't mix `then` and `await` syntax, rather make the outer function use `async`/`await` as well - can you show us more code? – Bergi Aug 27 '19 at 00:26
  • 1
    Or if you don't want to use `await`, it should be a simple `return myTable.create({…}).then(result => result._id);` in the `if` block. – Bergi Aug 27 '19 at 00:27
  • "rather make the outer function" - ok you have my attention...! and i added more code, but its really not much else to it. – edwardsmarkf Aug 27 '19 at 00:53
  • 1
    You should probably start with `const data = await yourTable.findAll( {...}); // more code` etc. Then for the `if`/`else`, when you cannot just `return`, assign to variable (or use a conditional operator). Put all this in an `async` function (or, if it already is inside a function, add the `async` keyword to that). – Bergi Aug 27 '19 at 00:57
  • i had to look up what IIFE and IIAFE meant. thank you for your suggestions. – edwardsmarkf Aug 28 '19 at 17:00
  • @edwardsmarkf That's why I put links on them in the answer :-) – Bergi Aug 28 '19 at 17:05