1

I'm have a recursive function which is used to get SQL files from a CodeCommit repo on AWS and run them in sequence. We need to wait for the previous file to complete before running the next one. If one of the SQL files fails, I need the catch block to return information on the file which failed.

What I'm seeing with my code at the moment is that the catch block is repeating once for each SQL file in the repo. From my understanding, the 'throw' statement should return to the catch block of the function that initially called the function. Could anyone point out what I'm doing wrong here?

const getFileData = async (newSQLFiles,processed=[]) => {
      try{
        if(newSQLFiles.length ===0){
          client.release();
          await pool.end().then(() => console.log('DB Connection pool closed.'))
          return processed;
        }
    
        var params = {
                filePath: newSQLFiles[0].relativePath, 
                repositoryName: 'testDBScripts' //Use environment variable
              };
    
        const data = await codecommit.getFile(params).promise();
        await runScripts(data);
        processed.push(newSQLFiles[0].relativePath)  
      }catch(err){
        console.log(err)
        throw [err,processed];
      }
      return await getFileData(newSQLFiles.slice(1),processed);
}

await getFileData(newSQLFiles)
.then(processed=>console.log("Following products are updated.",processed))
.catch(async ([e, file])=> {

    client.release();
    await pool.end().then(() => console.log('DB Connection pool closed.'))
    //await codePipelineJobFailed("SQL file " + file + " failed with : " + e)

    throw new Error("SQL file " + file + " failed with : " + e)}
)

2 Answers2

0

In JavaScript, when a function hits a return statement, it will stop the function form executing furthermore. Probably it's currently not working because right after the catch within the function, you are returning.

So, to solve your issue, I would do like so:


const getFileData = async (newSQLFiles,processed=[]) => {
      try{
        if(newSQLFiles.length ===0){
          client.release();
          await pool.end().then(() => console.log('DB Connection pool closed.'))
          return processed;
        }
    
        var params = {
                filePath: newSQLFiles[0].relativePath, 
                repositoryName: 'testDBScripts' //Use environment variable
              };
    
        const data = await codecommit.getFile(params).promise();
        await runScripts(data);
        processed.push(newSQLFiles[0].relativePath)  
      }catch(err){
        console.log(err)
        throw [err,processed];
      }
      await getFileData(newSQLFiles.slice(1),processed);
}

await getFileData(newSQLFiles)
.then(processed=>console.log("Following products are updated.",processed))
.catch(async ([e, file])=> {

    client.release();
    await pool.end().then(() => console.log('DB Connection pool closed.'))
    //await codePipelineJobFailed("SQL file " + file + " failed with : " + e)

    throw new Error("SQL file " + file + " failed with : " + e)}
)

Note that I've removed the return from the function's scope after the try catch block.

  • I'm getting the same issue after removing the return except I no longer have the file information in the catch block of the getFileData call – Alan Rochford Apr 01 '21 at 23:17
0

Your code contains many obvious misunderstandings of how to write practical and robust asynchronous programs in JavaScript. There are still a few things I would change about the code below but I cannot advise as information about codecommit.getFile and runScripts are not provided. If you have any questions about this answer, I am happy to help -

async function getFileData(files) {
  const result = []
  for (const f of files) {
    try {
      const data = await codecommit.getFile({
        filePath: f.relativePath,
        repositoryName: 'testDBScripts'
      }).promise()
      await runScripts(data)
      result.push(f.relativePath)
    }
    catch (e) {
      throw new Error("SQL file " + f + " failed with : " + e.message)
    }
  }
  return result
}

Using it looks like this -

getFileData(newSQLFiles)
  .then(console.log, console.error)
  .finally(_ => client.release())
  .finally(_ => pool.end())

Or if you prefer catch, this does the same thing -

getFileData(newSQLFiles)
  .then(console.log)
  .catch(console.error)
  .finally(_ => client.release())
  .finally(_ => pool.end())

Note .finally callback can also return a promise that properly sequences the program. See the example below -

const delay = (ms,x) =>
  new Promise(r => setTimeout(_ => console.log(x) || r(x), ms))
    
delay(1000,"a")
  .then(_ => delay(1000,"b"))
  .then(_ => delay(200, "result"))
  .finally(_ => delay(500,"client released"))
  .finally(_ => delay(1000,"pool closed"))
  .then(console.log, console.error)
a
b
result
client released
pool closed
result

If any promise in the sequence is rejected or an error is thrown, the .finally handlers are still called -

const delay = (ms,x) =>
  new Promise(r => setTimeout(_ => console.log(x) || r(x), ms))
    
delay(1000,"a")
  .then(_ => Promise.reject(Error("SQL FAILURE")))
  .then(_ => delay(200, "result"))
  .finally(_ => delay(500,"client released"))
  .finally(_ => delay(1000,"pool closed"))
  .then(console.log, console.error)
a
client released
pool closed
Error: SQL FAILURE
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • Much appreciated! I'm pretty new to javascript so thought a recursive method would be the only way to run the code in order. codecommit.getFile is just an in-built aws-sdk function to pull a code file from a repo. Thanks again! – Alan Rochford Apr 05 '21 at 17:13