1

I'm being told that "await is only valid in async function", even though it is in a async function. Here is my code:

async function uploadMultipleFiles (storageFilePaths,packFilePaths,packRoot) {
    return new Promise((resolve,reject) => {
        try {
            for (i in storageFilePaths) {
                await uploadFile(storageFilePaths[i],packFilePaths[i],packRoot) // error throws on this line
            }
            resolve("files uploaded")
        } catch {
            console.log(err)
            reject("fail")
        }
    })
}

Why is this happening when I made it an async function? Is it because I am using a for loop? If so, how can I get the expected outcome without this error?

etarhan
  • 4,138
  • 2
  • 15
  • 29
Beatso
  • 75
  • 1
  • 9
  • 3
    The relevant function here is the lambda you've declared for the promise, not uploadMultipleFiles. – Rup Aug 25 '20 at 10:45

3 Answers3

6

The function you define starting on line 1 is async.

The arrow function you define on line 2 and pass to the Promise constructor is not async.


You are also using the multiple promise anti-pattern. Get rid of the Promise constructor entirely. Just return the value when you have it. That's one of the main benefits of the async keyword.

async function uploadMultipleFiles(storageFilePaths, packFilePaths, packRoot) {
    try {
        for (i in storageFilePaths) {
            await uploadFile(storageFilePaths[i], packFilePaths[i], packRoot) // error throws on this line
        }
        return "files uploaded";
    } catch {
        console.log(err);
        throw "fail";
    }
}
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • When I call `uploadMultipleFiles` later in the code, it's in an async function and its called as `await uploadMultipleFiles`. If I get rid of the promise here, how would I be able to `await` it later? – Beatso Aug 25 '20 at 10:56
  • 1
    @pufferfish — `async` functions always return promises – Quentin Aug 25 '20 at 12:25
2

You can only use await inside of an async function, the error refers to the callback your passing to your new Promise (since you are entering a new function scope there).

async function uploadMultipleFiles (storageFilePaths,packFilePaths,packRoot) {
  return new Promise((resolve,reject) => { // <========= this arrow function is not async 
      try {                                // so you cant use await inside
          for (i in storageFilePaths) {
              await uploadFile(storageFilePaths[i],packFilePaths[i],packRoot) // error throws on this line
          }
          resolve("files uploaded")
      } catch {
          console.log(err)
          reject("fail")
      }
  })
}

The part where you try to construct a new Promise is actually redundant since an async function will resolve to a Promise anyways (read more here). So you could write your code as follows:

async function uploadMultipleFiles (storageFilePaths,packFilePaths,packRoot) {
  try {
      for (i in storageFilePaths) {
          await uploadFile(storageFilePaths[i],packFilePaths[i],packRoot) // error throws on this line
      }
      return "files uploaded"
  } catch {
      console.log(err)
      throw new Error("fail");
  }
}
etarhan
  • 4,138
  • 2
  • 15
  • 29
  • 1
    `reject("fail")` and `return "fail"` are not equivalent. A `reject` and a `throw` are equivalent. – t.niese Aug 25 '20 at 12:10
  • @t.niese true, resolve('fail') would be the equivalent in my case, good catch. I'll edit my answer – etarhan Aug 25 '20 at 12:11
  • Just some nitpicking `reject("fail")` and `throw new Error("fail");` are also not equivalent. `reject("fail")` and `throw "fail";` are or `reject(new Error("fail"))` and `throw new Error("fail")`. But I agree that in both cases an `Error` (or a class inheriting from it) should be used. – t.niese Aug 25 '20 at 12:31
  • @t.niese agreed, just don't like throwing strings in general, since it could lead to some issues as discussed here: https://stackoverflow.com/questions/11502052/throwing-strings-instead-of-errors. The new Error part was just my suggestion but is indeed not completely equivalent to the example shown in the original post ;) – etarhan Aug 25 '20 at 12:35
  • I agree with that, but if you refactor existing code, it should be equivalent. Because you never know if the rejected value is used as string somewhere. But it for sure should be mentioned that `new Error` is better. – t.niese Aug 25 '20 at 12:36
  • @t.niese fair enough, since an exact equivalent was provided in an answer above I'll keep this one like this for reference sake – etarhan Aug 25 '20 at 12:38
-2

The Promise callback isn't async

async function uploadMultipleFiles (storageFilePaths,packFilePaths,packRoot) {
    return new Promise(async (resolve,reject) => {
        try {
            for (i in storageFilePaths) {
                await uploadFile(storageFilePaths[i],packFilePaths[i],packRoot) // error throws on this line
            }
            resolve("files uploaded")
        } catch {
            console.log(err)
            reject("fail")
        }
    })
}
Daniel
  • 2,288
  • 1
  • 14
  • 22
  • 3
    Making the callback to `new Promise` an asynchronous function is a definite [anti-pattern](https://stackoverflow.com/questions/43036229/is-it-an-anti-pattern-to-use-async-await-inside-of-a-new-promise-constructor). – lonesomeday Aug 25 '20 at 10:55