'await' is not working in async function - javascript

The code below gives me the following error:
SyntaxError: await is only valid in async function
async function getLastTransaction()
{
paymentsApi.listPayments(locationId, opts).then(function(transactions)
{
if(transactions[transactions.length-1] === undefined)
return; //no new transaction yet
var last_transaction_id = transactions[transactions.length-1].id;
var last_transaction_in_queue;
try {
last_transaction_in_queue = JSON.parse(order_queue[0]).order_id;
} catch (e) {
last_transaction_in_queue = order_queue[0].order_id;
}
//check if latest transaction is the same as lastest transaction in queue
if(last_transaction_id !== last_transaction_in_queue) {
console.log(`new payment...`);
var obj = await createTransactionObject(transactions[transactions.length-1], () => {
order_queue.unshift(obj);
console.log('new added', order_queue);
});
}
I don't understand the error since I'm using await for the same function createTransactionObject() but in another piece of code.
For example, in the following code, I don't get the error, and still, I'm using await before createTransactionObject()
async function populateQueue(transaction_list) {
for(var i = 0; i < transaction_list.length; i++)
{
var transaction_json = await createTransactionObject(transaction_list[i], () => {});
order_queue.unshift(transaction_json);
} }

You need to change this line:
paymentsApi.listPayments(locationId, opts).then(function(transactions)
to this:
paymentsApi.listPayments(locationId, opts).then(async (transactions) =>
The anonymous function you supply to .then needs to be asynced, because you're using await in it.
You could also replace the line with this (maybe even better):
const transactions = await paymentsApi.listPayments(locationId, opts);
Because the getLastTransaction function is asynced.

First of all you get the error not because the getLastTransaction function is async but because the anonymous function .then(function(transactions) is not async and you use await inside of it. You can't do that.
Now note that simple redeclaring the function as async function(transactions) will be syntactically correct but will that work fine? What happens now is that getLastTransaction fires some async process in the background and never awaits the result. Is that what you want?
To fix that you have to ask yourself: what am I trying to achieve? Should getLastTransaction wait for whatever the inner function is doing? Then make use of that async declaration:
async function getLastTransaction() {
const transactions = await paymentsApi.listPayments(locationId, opts);
// Some other code here
return xyz;
}
This is under the assumption that the paymentsApi is async/await compatible. If it is not then you have to play with manually creating and returning Promise objects (in which case async declaration won't help much).

paymentsApi.listPayments(locationId, opts).then(function(transactions) should be
paymentsApi.listPayments(locationId, opts).then(async function(transactions) as await can only be used in an asynchronous function.
Better still, since you already have an async function at the top level, why don't you just await paymentsApi.listPayments(locationId, opts) instead of chaining it with a then?
async function getLastTransaction() {
const transactions = await paymentsApi.listPayments(locationId, opts);
// Do something with transactions
}

await keyword works when scope is having async keyword used, here .then accepts callback function that doesn't have async, so await becomes alien here.
Lets re-write your code in async-await style:
async function getLastTransaction()
{
// #1 this fixes to adopt the await style and fixes the problem
const transactions = await paymentsApi.listPayments(locationId, opts);
// your rest code goes here ...
if(last_transaction_id !== last_transaction_in_queue) {
//now do it like this, await will make sense now
const obj = await createTransactionObject(transactions[transactions.length-1]);
order_queue.unshift(obj);
}
}

Related

Awaiting to a normal function

Is it possible await to a function that is not a promise?
Maybe a function that cannot fail.
for example:
async function doWork() {
const newData = await cannotFail();
console.log("done: "+ newData);
}
function cannotFail() {
var data = //cannot fail code . . .
return data;
}
doWork();
I was thinking maybe with another syntax this option makes sense.
Consider these two examples
async function getData(){
return "some data";
}
async function doSomething() {
const data = await getData();
console.log(data);
}
doSomething()
function getData(){
return "some data";
}
async function doSomething(){
const data = getData();
console.log(data);
}
doSomething();
The result is exactly the same. You only use await when you want to wait for an asnyc function like it is synchronous. Normal functions already are synchronous.
And yes it is possible to use await on a normal function
function getData(){
return 'some data';
}
async function doSomething(){
const data = await getData();
console.log(data);
}
doSomething();
There is just no reason to.
It is possible to use await with an expression that does not evaluate to a promise. Whether that is useful, is questionable.
First of all, when await is followed by an expression that does not evaluate to a promise object, it is turned into one -- a resolved promise.
The main difference you get by using await with a non-promise or a resolved promise, is that await will make the async function return. This behaviour is not dependent on whether the promise is already resolved or not. await always postpones the further execution of the function by placing that resuming-job on the promise job queue.
See the difference here:
async function test() {
let a = 1; // Without await
console.log(a);
return 2;
}
test().then(console.log);
console.log("end of main, synchronous code");
And with await
async function test() {
let a = await 1; // With await
console.log(a);
return 2;
}
test().then(console.log);
console.log("end of main, synchronous code");
Note the different order of execution.
It's not necessary, but it is possible:
const newData = await Promise.resolve( cannotFail() );

How to assign variable to result of asynchronous class method that returns an object in its promise?

Looks nobody on internet describes similar problem, and similar solution is not working for me.
I am trying to scrape webpage so I created class for parser and one of the method looks like follows:
get chListUrl() {
return "http://www.teleman.pl/program-tv/stacje";
}
getChannels() {
var dict = {};
axios.get(this.chListUrl).then(function (response) {
var $ = cheerio.load(response.data);
var ile_jest_stacji = $('#stations-index a').length;
$('#stations-index a').each( (i,elem) => {
let href = $(elem).attr('href');
let kodstacji = href.replace(/\/program-tv\/stacje\//ig,'');
let nazwastacji = $(elem).text();
dict[nazwastacji]=kodstacji;
});
return dict;
}).catch(function (error) {
console.log(error);
return null;
}).finally(function() {
console.log("Koniec");
});
}
And problem is getChannels must be indirectly asynchronous because it contains axios BUT
let tm = new TM();
var a = tm.getChannels();
a is always undefined and it should be dictionary! Such construct means "assing to variable a result of execution of tm.getChannels()" so assignment should always be done AFTER whole function ends. Otherwise such syntax in language is useless because you will never be sure what value is stored in variable, and such errors are difficult to find and debug.
var a = await tm.getChannels();
NOT WORKING -> SyntaxError: await is only valid in async function (huh?)
adding async to getChannels() changes nothing.
Assing async to getChannels() and remove 'await' from assignment returns Promise{undefined} (huh?)
putting async before axios changes nothing as response is already handled by .then()
changing return dict to return await dict gives another "await is only valid in async function" (huh? axios is asynchronous)
I'm scratching my head over this for 2 weeks.
In Swift when something is return in completion handler it is assigned to variable in proper moment, why something returned by Promise not works the same way?
You need to be inside an async function to use the await expression:
The await operator is used to wait for a Promise. It can only be used inside an async function.
await operator on MDN
Example sourced from MDN:
async function f1() {
var x = await resolveAfter2Seconds(10);
console.log(x); // 10
}
f1();
Fixing your issue
class TM {
get chListUrl() {
return "http://www.teleman.pl/program-tv/stacje";
}
async getChannels() { // you need not use get syntax
let dict = {};
try { // we will be handling possible errors with try catch instead of reject
const response = await axios.get(this.chListUrl);
let $ = cheerio.load(response.data);
let ile_jest_stacji = $('#stations-index a').length;
$('#stations-index a').each( (i,elem) => {
let href = $(elem).attr('href');
let kodstacji = href.replace(/\/program-tv\/stacje\//ig,'');
let nazwastacji = $(elem).text();
dict[nazwastacji]=kodstacji;
});
return dict;
} catch(ex) {
console.log(ex);
return null
}
}
}
// let's make it work!
(async function() {
const tm = new TM()
const channels = await tm.getChannels()
// do whatever you want with channels
console.log(channels)
})()
Now, you're probably not going to call getChannels out of nowhere like this instead you will probably be inside a function that you yourself defined, you need to add the async keyword to this function. Whatever block function your code is in needs to be async.
If you want to use the async/await syntax you should remove the .then() syntax and you can resolve that way:
async getChannels() {
const response = await axios.get(this.chListUrl);
return response
}
You can learn more about async/await in the link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

`then` not a function error when invoking function returning Promise.all in await expression

I have a function with a loop that creates an array of promises that I pass to Promise.all, which I'm then attempting to return to an await expression in the main function.
When I take the returned object and attempt to invoke then on it (which I would have thought was a promise), it doesn't appear to be a promise anymore. I'm receiving the error
TypeError: respPromise.then is not a function
Here's a simplified version of what I'm doing
const run = async () => {
const fs = require('fs').promises
const createThreeFiles = () => {
const promiseArray = []
for (let i = 0; i < 3; i++) {
promiseArray.push(fs.writeFile(`file${i}.txt`, `This is the text for ${i}`))
}
return Promise.all(promiseArray)
}
const respPromise = await createThreeFiles()
respPromise
.then(fileInfos => {
console.dir(fileInfos)
})
.catch(error => {
console.error(error)
})
}
run()
If I change the createThreeFiles function and invoke then immediately off of Promise.all(promiseArray) there is no problem at all invoking then.
const createThreeFiles = () => {
const promiseArray = []
for (let i = 0; i < 3; i++) {
promiseArray.push(fs.writeFile(`file${i}.txt`, `This is the text for ${i}`))
}
return Promise.all(promiseArray)
.then(values => {...})...
// the above works without error.
}
So I think I must have something not quite right with the use of async/await, but for the life of me I'm not sure what. I've read the docs and looked for things to fix (and also I'm new to async/await) but so far, no luck. I've checked docs on Promise.all, await, and async as well as read a couple of articles. I've checked things like the following:
Make sure I'm invoking await in a function declared as async. Check.
Declare the createThreeFiles function as async. No change.
Do the immediate above and move Promise.all into an await expression. No change, and the linter complained that I'd already used await when I invoked createThreeFiles.
At this point, I'm just grasping at straws. I can move this logic back out of the function, but was trying to refactor and optimise my code, so tell me, how do I correctly return the promise of Promise.all when returned from a function invoked in an await expression?
If you await a promise inside an async function it reslves directly to the value and not to a promise.
So this
const respPromise = await createThreeFiles()
respPromise
.then(fileInfos => {
console.dir(fileInfos)
})
Should actually be this
const fileInfos = await createThreeFiles();
console.log(fileInfos);

Can async functions accept and execute callback functions?

I've been really intrigued about async / await syntax recently and I've been experimenting with it for a while.
In this particular problem, my goal is to execute the callback function after all the promises are made, if possible.
I had no idea how to apply my ideas in real life situations so I expected the function to look like this.
function endTask (task) {
// finalizes tasks
}
var taskCompleted = false;
async function doSomething (callback) {
const response = await fetch(some_url);
const task = await response.json();
if (task) {
taskCompleted = true;
}
if (typeof callback == "function" && taskCompleted) {
callback(task);
}
}
doSomething(endTask);
The purpose of the async/wait functionality is to reduce the complexity called Callback Hell. Async functions effectively removes the necessity of passing around callback functions.
However, one can still pass a callback function to an async function as a reference to be called later in his logic. This is perfectly alright as long as the developers do not go to the extent of wrapping Promises around async functions. That application goes against the concept of async/await.
Async functions always take parameters in real practice and some of them are callback functions which are to be called immediately or pass on to another function.
Shorthand version of your code:
function endTask (task) {
// finalizes tasks
}
async function doSomething () {
const response = await fetch(some_url);
const task = await response.json();
if (typeof callback == "function" && task) {
endTask (task);
}
}
doSomething();
I think if you already used async/await, maybe you don't need to use callback anymore. The purpose of async/await functions is to simplify the behavior of using promises, and avoid callback hell too.
Hope the example below could help.
function endTask (task) {
// finalizes tasks
}
async function doSomething (callback) {
const task = await fetch(some_url).then(res => res.json());
return task;
}
async function main() {
const task = await doSomething();
if (task) {
endTask()
}
}
Yes, async functions can take and call callbacks just like any other function, but that pretty much defeats the purpose of using an async function. There are some cases where there is a valid reason to do this (see Dave Newton's comment below), but this isn't one of them.
Instead, you should do something like this:
function endTask(task) {
// finalizes tasks
}
var taskCompleted = false;
async function doSomething() {
const response = await fetch(some_url);
const task = await response.json();
return task;
}
doSomething()
.then(task => {
if (task) {
taskCompleted = true;
}
endTask(task);
})
.catch(error => {
// handle error
});

The correct way to invoke an async function main?

I have some nodejs scripts - i.e. processes which do a job and complete, rather than run continuously.
I use async functions, for example:
const mysql = require('mysql2/promise');
...
async function main() {
var conn = await mysql.createConnection(config.mysql);
...
var [response, error] = await conn.execute(`
DELETE something
FROM some_table
WHERE field = ?
`, [value]);
...
Is the following code:
main().then(() => process.exit(0)).catch(err => { console.error(err); process.exit(1); });
the best/correct way to start execution of the async code?
(It works, but I want to make sure that I'm not leaving any holes which might cause surprises, such as exceptions being silently swallowed.)
Why does conn.execute() return an error (which I need to manually check) rather than throwing one?
The use of then together with async..await isn't necessary because it's syntactic sugar for then.
Entry point could be async IIFE (IIAFE):
(async () => {
try {
var conn = await mysql.createConnection(config.mysql);
...
var [response] = await conn.execute(`
SELECT something
FROM some_table
WHERE field = ?
`, [value]);
...
process.exit(0);
} catch (err) {
console.error(err);
process.exit(1);
}
})();
There also may be no need for process.exit(0) if the connection was closed.
Why does conn.execute() return an error (which I need to manually check) rather than throwing one?
It doesn't, and isn't conventional for promise-enabled functions to return an error in a result.
Callback-based execute uses error-first callback for errors. Promise-based execute can't throw an error because it returns a promise which is rejected in case of error.
As the documentation shows, the second element is fields and not error:
const [rows, fields] = await conn.execute('select ?+? as sum', [2, 2]);
It may return rejected promise which can be caught with try..catch inside async function in case of error.
The async function declaration defines an asynchronous function, which returns an AsyncFunction object. An asynchronous function is a function which operates asynchronously via the event loop, using an implicit Promise to return its result. But the syntax and structure of your code using async functions is much more like using standard synchronous functions.
You can also define async functions using an async function expression.
async function f() {
try {
let response = await fetch('http://no-such-url');
} catch(err) {
alert(err); // TypeError: failed to fetch
}
}
f();
you can also immediately call an asynce function using the syntax below
(async () => {
})();

Categories

Resources