"await is only valid in async function" in for loop - javascript

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?

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";
}
}

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");
}
}

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")
}
})
}

Related

Why can't try/catch handle error throw in Promise constructor

This code get's unhandledRejection error and I don't know why.
If the Error is thrown in try/catch, shouldn't it be caught by Catch Expression?
async function main () {
try {
await run(throwError)
} catch (error) {
console.log('main catch error', error);
}
}
async function run (callback) {
return new Promise(async resolve => {
await throwError()
});
}
async function throwError () {
throw new Error('custom error')
}
process.on('unhandledRejection', (reason, promise) => {
console.log('unhandledRejection - reason', reason, promise);
})
main()
It's not caught because you're passing an async function into new Promise. An error inside an async function rejects the promise that the function returns. The Promise constructor doesn't do anything a promise returned by the function you pass it (the return value of that function is completely ignored), so the rejection goes unhandled. This is one of the promise anti-patterns: Don't provide a promise to something that won't handle it (like addEventListener on the web, or the Promise constructor, or forEach, ...).
Similarly, there's no reason to use new Promise in your async function at all. async functions already return promises. That's another anti-pattern, sometimes called the explicit promise construction anti-pattern. (But see below if you're wrapping an old-fashioned callback API.)
If you remove the unnecessary new Promise, it works as you expect (I also updated run to call callback rather than ignoring it and calling throwError directly):
async function main() {
try {
await run(throwError);
} catch (error) {
console.log("main catch error", error);
}
}
async function run(callback) {
return await callback();
}
async function throwError() {
throw new Error("custom error");
}
process.on("unhandledRejection", (reason, promise) => {
console.log("unhandledRejection - reason", reason, promise);
});
main();
About the return await callback(); in that example: Because whatever you return from an async function is used to settle the function's promise (fulfilling it if you return a non-thenable, resolving it to the thenable if you return one), and that return is at the top level of the function (not inside a try/catch or similar), you could just write return callback();. In fact, you could even remove async from run and do that. But keeping the await makes the async stack trace clearer on some JavaScript engines, more clearly indicates what you're doing, and keeps working correctly even if someone comes along later and puts a try/catch around that code.
In a comment, you said that you used new Promise because you were wrapping around a callback-based API. Here's how you'd do that (see also the answers here):
// Assuming `callback` is a Node.js-style callback API, not an
// `async` function as in the question:
function run(callback) {
return new Promise((resolve, reject) => {
callback((err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
but, you don't have to do that yourself if the callback-based API uses the standard Node.js convention as above, there's a promisify function in utils.
You don't want to throw an error in this case, you want to invoke reject:
return new Promise((resolve, reject) => {
reject('custom error')
});
If the error being thrown is out of your control, you can catch it inside the promise implementation and reject in that case.

can't get an expect console output when using async await

function fails4() {
return new Promise((resolve, reject) => {
setTimeout(function () {
reject(new Error());
}, 100);
});
}
async function myFunc4() {
try {
await fails4();
} catch (e) {
console.log(e);
console.log('that failed', e); //<-- this gets called
}
}
async function loadmYScript() {
try {
await myFunc4();
} catch (error) {
console.log(error);
console.log(123);
}
}
loadmYScript();
cant't execute the console.log(123) as I expected can anybody help me with this question very appreciated
You're calling loadmYScript, which in turn calls myFunc4, which in turn calls fails4. This last one (fails4) throws an error. The error is "catch-ed" by myFunc4. Inside this catch block you don't throw any error, there's only a couple of logs, so the result of loadmYScript is a fulfilled promise with undefined value. It is not rejected because myFunc4 doesn't throw the error.
If you throw an error inside the catch block of myFunc4, you will have your 123 logged, and the promise will be rejected.

How to call an API twice if there is an error occurred?

I have an internal API that I would like to post data. Depends on some cases, I am seeing errors. So what I would like to do is to call it again if there is an error occurred.
What I did was to create a counter to pass it to the function and call the function recursively as below. This gives me the error as below:
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
Here is how I call the function:
....
private RETRY_API = 1;
....
try {
await this.callAPI(request, this.RETRY_API);
} catch (error) {
console.log('error', error);
}
This program never comes to the catch block above.
And here is my actual function that I call the API:
private async callAPI(request, retry) {
return new Promise((resolve, reject) => {
someService.postApiRequest('api/url', request, async(err: any, httpCode: number, data) => {
if (this.RETRY_API == 2) {
return reject(err);
} else if (err) {
this.callAPI(request, retry);
this.RETRY_API++;
} else if ( httpCode !== 200 ) {
this.RETRY_API = 2;
// some stuff
} else {
this.RETRY_API = 2;
// some stuff
return resolve(data);
}
});
})
}
Not sure what I am missing. If there is a better way to call the API twice if an error occurred, that would be great if you let me know.
Let's organize a little differently. First, a promise-wrapper for the api...
private async callAPI(request) {
return new Promise((resolve, reject) => {
someService.postApiRequest('api/url', request,(err: any, httpCode: number, data) => {
err ? reject(err) : resolve(data);
});
});
}
A utility function to use setTimeout with a promise...
async function delay(t) {
return new Promise(resolve => setTimeout(resolve, t));
}
Now, a function that calls and retries with delay...
private async callAPIWithRetry(request, retryCount=2, retryDelay=2000) {
try {
return await callAPI(request);
} catch (error) {
if (retryCount <= 0) throw err;
await delay(retryDelay);
return callAPIWithRetry(request, retryCount-1, retryDelay);
}
}
If you can't force a failure on the api to test the error path some other way, you can at least try this...
private async callAPIWithRetry(request, retryCount=2, retryDelay=2000) {
try {
// I hate to do this, but the only way I can test the error path is to change the code here to throw an error
// return await callAPI(request);
await delay(500);
throw("mock error");
} catch (error) {
if (retryCount <= 0) throw err;
await delay(retryDelay);
return callAPIWithRetry(request, retryCount-1, retryDelay);
}
}
It looks like you need to add return await to the beginning of the line this.callAPI(request, retry); in callAPI function.
Similarly there are some condition blocks that doesn't resolve or reject the promise. While it might work okay, it's considered bad practice. You want to either resolve or reject a promise.
I've accomplished calling an API a second time when I received an error by using axios' interceptors functions.
Here is a code snippet you can review:
axios.interceptors.response.use(
// function called on a successful response 2xx
function (response) {
return response;
},
// function called on an error response ( not 2xx )
async function (error) {
const request = error.config as AxiosRequestConfig;
// request is original API call
// change something about the call and try again
// request.headers['Authorization'] = `Bearer DIFFERENT_TOKEN`;
// return axios(request)
// or Call a different API
// const new_data = await axios.get(...).then(...)
// return new_data
// all else fails return the original error
return Promise.reject(error)
}
);
Try replacing
if (this.RETRY_API == 2)
with
if (this.RETRY_API > 1)

Why does line after return reject() execute in below function?

For the code below why does "A" and "B" get printed before the error 'a'
var a = async()=>{
return new Promise(async (resolve,reject)=>{
var v = await ab().catch((err)=>{
console.log("error")
return reject(err)
})
console.log("A")
console.log("B")
})
}
function ab(){
return new Promise((resolve,reject)=>{
return reject("a")
})
}
async function e(){
try{
await a()
}catch(e){
console.log(e)
}
}
e()
The output for the above code is
error
A
B
a
Shouldn't it be "error" followed by 'a'
Why are A and B getting printed?
Because there's no reason it shouldn't. The return is in a callback function, so it only returns from that callback function. It does reject the outer promise, but doing that doesn't prevent the remainder of the code in your promise executor (the function you pass new Promise) from continuing.
There are several problems with the code shown, though.
If you already have a promise, you don't need to create a new one. In a, for instance, you have the promise from ab, so there's no reason for new Promise. This is often called the explicit Promise creation antipattern, this question's answers go into detail.
Using await in a promise executor function generally doesn't make much sense and probably means the executor is doing too much. The purpose of a promise executor is to start an asynchronous process, not to wait for one.
Using .catch/.then/.finally inside an async function is generally not best practice; just use await and try/catch.
Your a function should probably be just:
const a = async () => {
try {
await ab();
// Or you might put the `console.log`s here, there are
// reasons for each, but it doesn't matter given we're
// rethrowing the error
} catch (err) {
console.log("error")
throw err;
}
console.log("A")
console.log("B")
};
That will reject the promise a returns when ab's promise rejects, using the same rejection reason, and terminate a's logic rather than allowing it to continue; if ab's promise doesn't reject, it will run the two console.log statements.
Live Example:
const a = async () => {
try {
await ab();
// Or you might put the `console.log`s here, there are
// reasons for each, but it doesn't matter given we're
// rethrowing the error
} catch (err) {
console.log("error")
throw err;
}
console.log("A")
console.log("B")
};
function ab() {
return new Promise((resolve, reject) => {
return reject("a");
});
}
async function e() {
try {
await a();
} catch (e) {
console.log(e);
}
}
e();

Node.JS - Can`t get async throws with try/catch blocks

When I create an async function in node and use await, I'm making the execution waits for a promise resolution (that can be a resolve or a rejection), what I do is put an await promise inside a try/catch block and throw an error in case of a promise rejection. The problem is, when I call this async function inside a try/catch block to catch the error in case of it, I get an UnhandledPromiseRejectionWarning. But the whole point of using await isn't waiting for the promise to resolve and return it's result? It seems like my async function is returning a promise.
Example - The code an UnhandledPromiseRejectionWarning:
let test = async () => {
let promise = new Promise((resolve, reject) => {
if(true) reject("reject!");
else resolve("resolve!");
});
try{
let result = await promise;
}
catch(error) {
console.log("promise error =", error);
throw error;
}
}
let main = () => {
try {
test();
}
catch(error){
console.log("error in main() =", error);
}
}
console.log("Starting test");
main();
async functions always return promises. In fact, they always return native promises (even if you returned a bluebird or a constant). The point of async/await is to reduce the version of .then callback hell. Your program will still have to have at least one .catch in the main function to handle any errors that get to the top.
It is really nice for sequential async calls, e.g.;
async function a() { /* do some network call, return a promise */ }
async function b(aResult) { /* do some network call, return a promise */ }
async function c() {
const firstRes = (await (a() /* promise */) /* not promise */);
const secondRes = await b(firstRes/* still not a promise*/);
}
You cannot await something without being inside a function. Usually this means your main function, or init or whatever you call it, is not async. This means it cannot call await and must use .catch to handle any errors or else they will be unhandled rejections. At some point in the node versions, these will start taking out your node process.
Think about async as returning a native promise - no matter what - and await as unwrapping a promise "synchronously".
note async functions return native promises, which do not resolve or reject synchronously:
Promise.resolve(2).then(r => console.log(r)); console.log(3); // 3 printed before 2
Promise.reject(new Error('2)).catch(e => console.log(e.message)); console.log(3); // 3 before 2
async functions return sync errors as rejected promises.
async function a() { throw new Error('test error'); }
// the following are true if a is defined this way too
async function a() { return Promise.reject(new Error('test error')); }
/* won't work */ try { a() } catch(e) { /* will not run */ }
/* will work */ try { await a() } catch (e) { /* will run */ }
/* will work */ a().catch(e => /* will run */)
/* won't _always_ work */ try { return a(); } catch(e) { /* will not usually run, depends on your promise unwrapping behavior */ }
Main must be an async function to catch async errors
// wont work
let main = () =>{
try{
test();
}catch(error){
console.log("error in main() =", error);
}
}
// will work
let main = async () =>{
try{
test();
}catch(error){
console.log("error in main() =", error);
}
}

Categories

Resources