async resolves before try catch runs - javascript

I have this cloud function:
const initCompress = async () => {
try {
logger.log('This shows before the function terminated');
await mkdirp('../../../xxxxx'); <<<< WHY IS IT NOT WAITING FOR THIS TO COMPLETE?
logger.log('This shows after the function terminated');
return { success: true };
} catch (err) {
...
} finally {
...
}
};
export default initCompress;
It is executed via:
const main = async () => {
exports.compressImage = functions.https.onCall((data, context) => {
initCompress(data, context);
});
}
The console log shows:
functions: Beginning execution of "compressImage"
> {"severity":"INFO","message":"This shows before the function terminated"}
functions: Finished "compressImage" in ~1s
> {"severity":"INFO","message":"This shows after the function terminated"}
I'm probably doing something silly, but what? Do I need to await something somewhere?

What you observe is totally normal. By declaring a function as async, your make it return a Promise. That Promise is being returned immediately (it just doesn't resolve immediately). Your code is equivalent of this:
const mkdirp = () => Promise.resolve();
const logger = console;
const modifyImage = () => {
logger.log('This shows before the function terminated');
return mkdirp('../../../xxxxx')
.then(() => logger.log('This shows after the function terminated'))
.then(() => ({ success: true }))
.catch(logger.error);
};
modifyImage().then(logger.log);
logger.log('Function finished');
As a conclusion, never forget that async/await is just syntaxic sugar over Promises

Related

Curry function with the first one as an async function isn't working

I'm trying to curry a function but when the first one is async it throws the error function1(...) is not a function, however if I pass the async function as the last one it works fine.
Can anyone tell me why this is happening? and how to properly make a curry function that starts with a async function?
Thanks to anyone who take the time.
//This one is throwing the error: function1(...) is not a function
async function function1(path) {
const fetchedElement = await fetchElement(path);
//(...)
return (msg) => {
console.log(msg);
};
}
function1('somepath.html')('my message');
//This one works fine, properly returning other function
function function2(path) {
return async (msg) => {
const fetchedElement = await fetchElement(path);
//(...)
console.log(msg);
};
}
function2('somepath.html')('my message');
It depends on when the async work needs to get done. If you want the currying process to do the async work, then you can't invoke the currying function synchronously:
curryingFunction(paramA)(paramB)
^ assumes curryingFunction is synchronous
Instead, use two statements by the caller, one to await the currying, the other to invoke...
const fetch = path => new Promise(resolve => setTimeout(() => {
console.log('fetched ' + path)
resolve()
}, 1000));
async function curryingFunction(path) {
const fetchedElement = await fetch(path);
return message => {
console.log(message);
}
}
async function someCaller() {
// await to get a curried function, then invoke it
let curried = await curryingFunction('some_path');
curried('some message');
}
someCaller()
On the other hand, you might not need to do the async work in order to get the curried function. You probably don't. In that case, you can make the currying function synchronous, but have it return an async function that does the async work.
As a result, you'll get to use the fn()() syntax that you're probably used to using...
const fetch = path => new Promise(resolve => setTimeout(() => {
console.log('fetched ' + path)
resolve()
}, 1000));
function curryingFunction(path) {
return async (message) => {
const fetchedElement = await fetch(path);
console.log(message);
}
}
async function someCaller() {
// here we can use the fn()() syntax that we're accustomed to
await curryingFunction('some path')('some message')
}
someCaller()
As the first function is async You need to await it first, then call the second parameter.
//This one is throwing the error: function1(...) is not a function
async function function1(path) {
const fetchedElement = await Promise.resolve(1);
//(...)
return (msg) => {
console.log(msg);
};
}
(async () => {
try {
(await function1('somepath.html'))('my message');
} catch(e) {
console.log(e)
}
})()

Using useEffect with async?

I'm using this code:
useFocusEffect(
useCallback(async () => {
const user = JSON.parse(await AsyncStorage.getItem("user"));
if (user.uid) {
const dbRef = ref(dbDatabase, "/activity/" + user.uid);
onValue(query(dbRef, limitToLast(20)), (snapshot) => {
console.log(snapshot.val());
});
return () => {
off(dbRef);
};
}
}, [])
);
I'm getting this error:
An effect function must not return anything besides a function, which
is used for clean-up. It looks like you wrote 'useFocusEffect(async ()
=> ...)' or returned a Promise. Instead, write the async function inside your effect and call it immediately.
I tried to put everything inside an async function, but then the off() is not being called.
Define the dbRef variable outside the nested async function so your cleanup callback can reference it, and allow for the possibility it may not be set as of when the cleanup occurs.
Also, whenever using an async function in a place that doesn't handle the promise the function returns, ensure you don't allow the function to throw an error (return a rejected promise), since nothing will handle that rejected promise.
Also, since the component could be unmounted during the await, you need to be sure that the async function doesn't continue its logic when we know the cleanup won't happen (because it already happened), so you may want a flag for that (didCleanup in the below).
So something like this:
useFocusEffect(
useCallback(() => {
let dbRef;
let didCleanup = false;
(async() => {
try {
const user = JSON.parse(await AsyncStorage.getItem("user"));
if (!didCleanup && user.uid) {
dbRef = ref(dbDatabase, "/activity/" + user.uid);
onValue(query(dbRef, limitToLast(20)), (snapshot) => {
console.log(snapshot.val());
});
}
} catch (error) {
// ...handle/report the error...
}
})();
return () => {
didCleanup = true;
if (dbRef) {
off(dbRef);
}
};
}, [])
);

How to wait for a function, which contains promises

If we have this function
const getData = () => {
foo()
.then(result => {
return result;
})
.catch(error => {
return error;
});
};
Although getData is not a promise itself, but it contains a promise, which is asyncrnous.
So what is the best way to wait for getData to return something. Async / Await doesn't work cause they work with promises.
Thanks.
Currently, getData() doesn't return anything. You need to make it return a Promise, so you can await it or chain .then() to it.
const getData = () => {
return foo() // <-- here
.then(result => {
return result;
})
.catch(error => {
throw error;
});
};
// Now you can do :
getData().then(...)
// or :
const data = await getData();
In this case, you can also omit the curly braces and the explicit return, and make it implicit :
const getData = () => foo()
.then(result => {
return result;
})
.catch(error => {
throw error;
});
Hey but what's that :
.then(result => {
return result;
})
This does nothing. It takes a value and simply returns it without doing anything. You can remove it.
You can now rewrite getData() this way :
const getData = async () => {
try {
return await foo()
} catch (error) {
throw error;
}
}
For that matter, this :
.catch(error => { throw error; });
or this :
catch (error) { throw error; }
are also pretty useless, they just "relay" (bubble up) the error that has to be caught in the calling function.
Now it's obvious getData does pretty much only one thing, it's returning foo(), which is a Promise. It's only a wrapper around a Promise... so it's actually pretty useless.
Bottom line, detData() is useless altogether. foo is a Promise; writing a function that returns a Promise so you can use it like a Promise is just... a Promise with an extra step. Just use foo directly.
let result;
try {
result = await foo();
} catch (error) {
console.log(error);
}
console.log(result);
This will not work because getData is not returning a value. You can add a return statement before foo call and wait for the return value.
const getData = () => {
return foo();
};
getData().then(data => {
console.log(data);
}).catch(err => {
console.log(err);
});
To wait for an operation you must return a Promise or use a callback. The code snippet below runs and should illustrate how this works. I implemented a sample foo function that is actually asynchronous (wait for 1 second before returning the data '12345'). I used async/await to illustrate how that can work, but you can equally return the result of foo and use then instead.
const foo = () => {
return new Promise(resolve => {
setTimeout(() => resolve('12345'), 1000);
});
}
const getData = async () => {
const data = await foo();
console.log(`Data is ${data}`);
return data;
};
getData()
.then(() => console.log('complete'))
.catch(err => console.log(`oops: ${err}`));
console.log('this prints first since async operation is still pending');

Trying to execute an imported Async function but the function is not behaving asynchronously

I am using React to build a website. I have imported an asynchronous function to execute when I press a button. However, the function is not working asynchronously and I really don't understand why.
interact.js:
export const getNFT = async () => {
setTimeout(() => {
console.log('getNFT code execute');
return nft;
}, 2000);
};
const nft = {
tokenURI: 'https://gateway.pinata.cloud/ipfs/QmdxQFWzBJmtSvrJXp75UNUaoVMDH49g43WsL1YEyb',
imageURL: 'https://gateway.pinata.cloud/ipfs/QmeMTHnqdfpUcRVJBRJ4GQ2XHU2ruVrdJqZhLz',
ID: '212'
};
Main.js
import {
getNFT
} from 'interact.js';
// This function is executed when a user clicks on a button
let getAllocatedNFT = async () => {
try {
let response = await getNFT();
console.log('response from server:: '+response);
}catch(e){
console.log(e);
}
};
console:
response from server:: undefined
getNFT code execute // This is executed correctly after 2 seconds
You have to return promise which will resolve your webAPI(setTimeout)
Please use like below:
const getNFT = async () => {
return new Promise(resolve => setTimeout(() => {
console.log("getNFT code execute")
resolve(true)
}, 2000)
);
};

Unsure about usage of async and callbacks in javascript

Description:
I have a Mocha Test within my Node-App that should test whether a DB-Export of Mongo-DB-JSON-Documents is done correctly.
In my test I besides other tests also test if the download-directory is not empty.
Expected result:
The test should await the downloads and only then check whether the directory is empty.
Actual Result:
The test returns always green.
My Question:
I understood that we have callbacks but promises are better.
I understood that async await is some syntactic sugar to promises.
And I understood that there is even RxJS (which I do not use here)
Somehow I have to deal with the callback from mogodb-backup.
See https://www.npmjs.com/package/mongodb-backup
I do not understand what I am doing wrong so that the tests always turn green (running in parallel to the download)
mocha-test.js
describe('Database.downloadDocumentsOfType_KEYS()', function () {
it('should result in data/exportFromCosmos/KEYS/admin/ag-data/ not being empty', function () {
const config = {
documents: ['DEFAULT', 'KEYS'],
exportpathDEFAULT: 'data/exportFromCosmos/DEFAULT/',
exportpathKEYS: 'data/exportFromCosmos/KEYS/',
uploadpath: 'data/uploadToAzureBlob/',
crosscheckFile: 'data/crosscheckFile.txt'
}
async function f() {
await Database.downloadDocumentsOfType_KEYS().then(expect(dir(config.exportpathKEYS + 'admin/ag-data/')).to.not.be.empty)
}
f()
})
})
Databasemodule-to-be-tested.js
const mongodbbackup = require('mongodb-backup')
const Database = {
downloadDocumentsOfType_DEFAULT: () => {
new Promise((resolve) => mongodbbackup({
uri: process.env.DB_CONNECTION_STRING_READ,
root: 'data/exportFromCosmos/DEFAULT',
parser: 'json',
callback: function(err) {
if (err) {
reject()
} else {
resolve()
}
}
)}
}
async function f() {
await Database.downloadDocumentsOfType_KEYS().then(e)
}
f()
This fires off the asynchronous function immediately and
it('...', function (){})
finishes immediately.
So you need to use
describe('...',async function(){
it('...',async function(){
const f = async function(){
await Database.downloadDocumentsOfType_KEYS();
expect(dir(config.exportpathKEYS + 'admin/ag-data/')).to.not.be.empty);
};
await f();
});
});
Also,
new Promise((resolve) => mongodbbackup({...
should be
new Promise((resolve,reject) => mongodbbackup({
Otherwise reject is undefined

Categories