The idea:
I am building a bot to log into Instagram using puppeteer.If the login is successful, nothing happens. When an error with ID slfErrorAlert occurs, the bot stops (because the LogIn was not successful).
The problem:
After pressing the LogIn button, the first and afterwards the second statement is checked.
However, it should be checked at the same time whether one or the other statement is true.
This could be the pseudo code:
If (Login == true) continue;
else if (slfErrorAlert == visible) stop;
Current code snippet
if ((await instagram.page.url() !== loginURL) || (await instagram.page.waitForSelector('#slfErrorAlert'))) {
if (await instagram.page.url() !== loginURL) {
log.info("LogIn successfull")
log.info(instagram.page.url())
}
if (await instagram.page.waitForSelector('#slfErrorAlert')) {
let loginMessage = await instagram.page.$eval('#slfErrorAlert', element => element.innerHTML)
log.warn(`Client error - LogIn not possible: '${loginMessage}'`)
await instagram.browser.close()
return
}
}
Full code is here: https://github.com/JueK3y/Instagram-automated-commenting/blob/main/public/src/js/instagram.js
If i understand you correctly, you want to test both async calls simultaneously. You can do that with the function Promise.race.
let promise1 = instagram.page.url();
let promise2 = instagram.page.waitForSelector('#slfErrorAlert');
Promise.race([promise1, promise2]).then((result) => {
// do your checks here, either of the 2 promises was resolved
// the result is either the one return from the first or second promise
})
For a detailed explanation and an extended example on how to use it, see MDN web docs: Promise.race()
Related
For a specific function, a list of tags (rfids) are required from a database, but various tables should be checked.
The issue is centered around the problem of a long running operation isn't awaited upon to finish before proceeding to the next.
Problem:
The code called below runs as follows (# numbers correspond to number in code below):
getAllTags() is called, starting the proc
await db.dbRfid.findAll() returns with X amount of valid results (not Promise objects)
start filtering call on valid objects & await finish (await place since some function calls inside are async calls.
call const driverTags = await tag.getDrivers();
At this point, one would expect this function to return the result and proceed to the next function i.e.
// #5
const truckTags = await tag.getTrucks();
What infact happens is for each of the allTags items, the getDrivers() gets called and the filter() exits and the code continues on by executing this next:
// #6
if (pureTags) {
return filteredTags;
}
Question:
To my understanding, I am awaiting async operations correctly, yet it seems like the filter only accepts/allows one async operation.
I assume I did something wrong, yet I am unable to the cause of the problem. Any advice would be appreciated!
Full code implementation below:
Called with e.g.
const listOfTags = await getAllTags();
Specific tag requirements (all valid, unused, enabled & unrevoked tags)
const listOfTags = await getAllTags(false, true, true, true);
Problem code:
// #1
const getAllTags = async (pureTags = false, unused = true, enabled = true, notRevoked = false) => {
// #2
let allTags = await db.dbRfid.findAll()
// #3
let filteredTags = await allTags.filter(async tag => {
// check tag used
if (unused) {
// #4
const driverTags = await tag.getDrivers();
// #5
const truckTags = await tag.getTrucks();
const userTags = await tag.getUsers();
if (driverTags.length > 0) {
return false;
}
if (truckTags.length > 0) {
return false;
}
if (userTags.length > 0) {
return false;
}
}
// check tag enabled
if (enabled && !tag.enabled) {
return false;
}
// check tag revoked or return true
return notRevoked && !tag.revoked;
});
// return tags as is
// #6
if (pureTags) {
return filteredTags;
}
return filteredTags.map(tag => {
return {
id: tag.id,
rfid: tag.rfid,
expiryDate: tag.expiryDate,
revoked: tag.revoked,
enabled: tag.enabled
}
});
}
Update
I should mention:
no 'errors' of any sort are shown to indicate of some problem.
the .getDrivers() is a getter created by sequelize which returns a promise.
Update 2
After comment by evgeni fotia
I originally had this code, however chose not to include it as it may havve complicated matters. However, this it the original code I attempted with the addition of the await Promise.all().
const getAllTags = async (pureTags = false, unused = true, enabled = true, notRevoked = false) => {
let allTags = await db.dbRfid.findAll()
let filteredTags = await Promise.all(
// await allTags.filter(async tag => { //tried the await here too - for good measure
allTags.filter(async tag => {
// check tag used
if (unused) {
const driverTags = await tag.getDrivers();
const truckTags = await tag.getTrucks();
const userTags = await tag.getUsers();
if (driverTags.length > 0) {
return false;
}
if (truckTags.length > 0) {
return false;
}
if (userTags.length > 0) {
return false;
}
}
// check tag enabled
if (enabled && !tag.enabled) {
return false;
}
// check tag revoked or return true
return notRevoked && !tag.revoked;
})
);
// return tags as is
if (pureTags) {
return filteredTags;
}
return filteredTags.map(tag => {
return {
id: tag.id,
rfid: tag.rfid,
expiryDate: tag.expiryDate,
revoked: tag.revoked,
enabled: tag.enabled
}
});
}
Please note, after running the code in this update or in the original question, I see the debugger hitting the getDrivers() method, then the (HTTP) response is sent to the client, and only after 0.5~1s I see the getDrivers() method returning and proceeding to the next method.
Your central problem is, as T.J. Chowder has commented, that you are trying to .filter() on something that is sometimes a boolean, and sometimes a promise.
You should do the mapping and the filtering in different steps. I tend to prefer the .then() syntax, so here's my approach:
const getAllTags = (pureTags = false, unused = true, enabled = true, notRevoked = false) => {
return db.dbRfid.findAll()
.then(allTags => allTags.map(tag => Promise.resolve(
enabled === tag.enabled && notRevoked === !tag.revoked && Promise.all([
tag.getDrivers(), tag.getTrucks(), tag.getUsers()
]).then(results => results.some(r => r.length))
).then(ok => ok ? tag : null)))
.then(pendingTags => Promise.all(pendingTags))
.then(resolvedTags => resolvedTags.filter(tag => tag))
.then(filteredTags => filteredTags.map(tag => pureTags ? tag : {
id: tag.id,
rfid: tag.rfid,
expiryDate: tag.expiryDate,
revoked: tag.revoked,
enabled: tag.enabled
}));
};
The logic of this code:
fetch tags from DB
produces Promise<Tag[]>, i.e. a promise for an array of tags
map each tag to a new promise that resolves either to the tag itself, or null, depending on a condition we figure out using Promise.resolve(), to account for the potentially(*) async nature of that check
produces Promise<Promise<Tag|null>[]>
wait for all those inner promises to resolve using Promise.all()
produces <Promise<(Tag|null)[]>
filter out the null values (i.e. tags we don't want)
produces <Promise<Tag[]>
map the tags to the overall result data structure we want, depending on pureTags
produces <Promise<(Tag|object)[]>
(*) The Promise.all() is only invoked if the preconditions check out, due to short circuiting. If not, it's simply a Promise.resolve(false), which resolves to false immediately. In the other case it's going to be Promise.resolve(<Promise<Boolean>), which works exactly the same way as Promise.resolve(Boolean). This way we can unify the synchronous and the asynchronous test.
In the subsequent .then() we can decide whether to return the tag - or null, to indicate that this tag failed the filter conditions. The null values are then picked out by resolvedTags.filter(tag => tag).
Note that your code serializes the three async checks (getDrivers, getTrucks, getUsers) because each is awaited before the next is started. Using Promise.all() lets them run in parallel, which is one of the reasons why I dislike using async/await for everything.
I leave rewriting this into async/await style as an exercise.
I need to wait for mapping function to finish before I send the data to the console. I know it has something to do with Promise. I've been trying for hours and I couldn't get it to work even after reading so much about promises and async functions...
async function inactiveMemberWarner() {
var msg = "```javascript\nI have sent warnings to members that have been inactive for 2 weeks.\n\n"
var inactiveMembers = '';
var count = 0;
var guildMembers = client.guilds.find(g => g.name === mainGuild).members;
const keyPromises = await guildMembers.map(async (member) => {
if (isMod(member)) {
connection.query(`SELECT * from users WHERE userID='${member.id}'`, (err, data) => {
if (data[0]) {
if (!data[0].warnedForInactivity && moment().isSameOrAfter(moment(data[0].lastMSGDate).add('2', 'week'))) {
count++;
var updateWarning = {warnedForInactivity: 1}
connection.query(`UPDATE users SET ? WHERE userID='${data[0].userID}'`, updateWarning);
member.send(`**[*]** WARNING: You've been inactive on \`\`${mainGuild}\`\` for 2 weeks. Members that have been inactive for at least a month will be kicked.`);
inactiveMembers += `${count}. ${member.user.tag}\n`;
return inactiveMembers;
}
}
});
}
});
await Promise.all(keyPromises).then(inactiveMembersData => console.log(inactiveMembers)); // RETURNS AN EMPTY STRING
setTimeout(() => console.log(inactiveMembers), 5000); // RETURNS THE INACTIVE MEMBERS AFTER WAITING FOR 5 SECONDS (PRMITIVE WAY)
}
inactiveMemberWarner();
Thank you in advance!
You're close, but not quite there.
First, some notes:
await can be used on any value, but it is entirely pointless to use it on anything that isn't a Promise. Your guildMembers.map(...); returns an array, not a Promise.
Mixing await and .then(...) works, but is kinda messy. You're already using await - why bother dealing with callbacks?
Using guildMembers.map(async ...) like this will ensure that all the requests are fired more or less instantaneously, and they could finish in any order. This is fine, but it is kind of a race condition and results in a more or less random order of results.
This is not a good approach even just conceptually! Any time you ever have to loop queries, try and investigate ways to do it in only one query. SQL is quite powerful.
The reason your current code doesn't work is because your connection.query function escapes the async control flow. What I mean by this is that the whole point of using async/await and Promises is basically to keep track of the callbacks locally, and to make use of promise chaining to dynamically add callbacks. If you call an async function which returns a Promise, you can now carry that Promise anywhere else in your code and attach a success handler to it dynamically: either with .then() or with the sugar await.
But the connection.query function doesn't return a Promise, it just has you pass another naked callback - this one is not being tracked by a Promise! The Promise doesn't have a reference to that callback, it can't know when that callback is getting called, and thus your async/await control flow is escaped and your promises resolve long before the queries have ran.
You can resolve this by making a new Promise in the async function:
async function inactiveMemberWarner() {
var msg = "```javascript\nI have sent warnings to members that have been inactive for 2 weeks.\n\n"
var inactiveMembers = '';
var count = 0;
var guildMembers = client.guilds.find(g => g.name === mainGuild).members;
const keyPromises = guildMembers.map(async (member) => {
if (isMod(member)) {
return new Promise((resolve, reject) => {
connection.query(`SELECT * from users WHERE userID='${member.id}'`, (err, data) => {
if (err) reject(err); //make errors bubble up so they can be handled
if (data[0]) {
if (!data[0].warnedForInactivity && moment().isSameOrAfter(moment(data[0].lastMSGDate).add('2', 'week'))) {
count++;
var updateWarning = {warnedForInactivity: 1}
connection.query(`UPDATE users SET ? WHERE userID='${data[0].userID}'`, updateWarning);
member.send(`**[*]** WARNING: You've been inactive on \`\`${mainGuild}\`\` for 2 weeks. Members that have been inactive for at least a month will be kicked.`);
resolve(`${count}. ${member.user.tag}\n`;);
}
} else resolve(""); //make sure to always resolve or the promise may hang
});
});
}
});
let inactiveMembersData = await Promise.all(keyPromises); // Returns an array of inactive member snippets.
inactiveMembers = inactiveMembersData.join(""); //join array of snippets into one string
}
inactiveMemberWarner();
This will work, but there is a much much much better way. SQL supports the IN operator, which allows you to have conditions like WHERE userID IN (list_of_ids). In other words, you can do this in one query. You can even specify more conditions, such as warnedForInactivity = 0 and lastMSGDate BETWEEN (NOW() - INTERVAL 14 DAY) AND NOW(). This way you can offload all of your current processing logic onto the SQL server - something that you should try to do virtually every single time you can. It would simplify this code a lot too. I won't go any further as it's out of scope for this question but feel free to ask another if you can't figure it out.
I can't test this, but this is what normally works for me is when wanting to wait on smt:
async function inactiveMemberWarner() {
new Promise(function(cb,rj){
var msg = "```javascript\nI have sent warnings to members that have been inactive for 2 weeks.\n\n"
var inactiveMembers = '';
var count = 0;
var guildMembers = client.guilds.find(g => g.name === mainGuild).members;
const keyPromises = await guildMembers.map(async (member) => {
if (isMod(member)) {
connection.query(`SELECT * from users WHERE userID='${member.id}'`, (err, data) => {
if (data[0]) {
if (!data[0].warnedForInactivity && moment().isSameOrAfter(moment(data[0].lastMSGDate).add('2', 'week'))) {
count++;
var updateWarning = {warnedForInactivity: 1}
connection.query(`UPDATE users SET ? WHERE userID='${data[0].userID}'`, updateWarning);
member.send(`**[*]** WARNING: You've been inactive on \`\`${mainGuild}\`\` for 2 weeks. Members that have been inactive for at least a month will be kicked.`);
inactiveMembers += `${count}. ${member.user.tag}\n`;
cb(inactiveMembers);
}
}
});
}
});
cb('No Members');
}).then(inactiveMembersData => console.log(inactiveMembers)); // SHOULD RETURNS THE INACTIVE MEMBERS
}
inactiveMemberWarner();
This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 3 years ago.
await is not blocking as expected, when a block of code updates db (using postgres / node )
https://node-postgres.com
I have a list of async function calls, each call udpates a database, and each subsequent call works on data updated by the previous call.
There are about eight calls in a row, and each call must update the complete set of data it is working with, 100% to completion, before going to the next.
I tried to make everything not async, but it appears I am forced to make everything async/await because of the library I am using (postgres / node).
Each function call must complete 100% before going on to the next function call, because the next step does a select on rows where a field is not null (where the previous step fills in a value).
I have an await in front of each call, that does something (see code below):
loads the db from a csv,
next step selects all rows just inserted, calls an API and updates the database,
and so on,
but at one point, when the next function executes, NONE of the rows have been updated (as I trace through and verify, a SQL statement returns nothing back),
the code seems to pass right through going to the second function call, not blocking, honoring the await, and completing it's code block.
If I comment out some of the latter rows (dependent on the previous), and let the program run to completion, the database gets updated.
There is nothing functionally wrong with the code, everything works, just not from beginning to completion.
After running two function calls at the beginning, letting that run, I can then comment out those rows, uncomment the later rows in the flow, and run again, and everything works as expected, but I cannot run to completion with both uncommented.
What can I do to make sure each function call completes 100%, has all updates completed in the database, before going to the next step?
async/await is not working for me.
this is not pseudo-code it's the actual code, that is executing, that I am working with, the function names changed only. It is real working code, cut-n-pasted direct from my IDE.
// these are functions I call below (each in their own .js)
const insert_rows_to_db_from_csv = require('./insert_rows_to_db_from_csv')
const call_api_using_rows_from_function_above = require('./call_api_using_rows_from_function_above')
const and_so_on = require('./and_so_on')
const and_so_on_and_on = require('./and_so_on_and_on')
const and_so_on_and_on_and_on = require('./and_so_on_and_on_and_on')
// each of the above exports a main() function where I can call func.main() just // like this one defined below (this is my main() entry point)
module.exports = {
main: async function (csvFilePath) {
console.log('service: upload.main()')
try {
const csvList = []
let rstream = fs.createReadStream(csvFilePath)
.pipe(csv())
.on('data', (data) => csvList.push(data))
.on('end', async () => {
let num_rows = csvList.length
//step one (if I run these two, with step two calls below commented out, this works)
await insert_rows_to_db_from_csv.main(csvList);
await call_api_using_rows_from_function_above.main();
// step two
// blows up here, on the next function call,
// no rows selected in sql statements, must comment out, let the above run to
// completion, then comment out the rows above, and let these run separate
await work_with_rows_updated_in_previous_call_above.main(); // sets
await and_so_on.main();
await and_so_on_and_on.main();
await and_so_on_and_on_and_on.main();
})
} catch (err) {
console.log(err.stack)
} finally {
}
}
};
here is the one liner I am using to call the insert/update to the DB:
return await pool.query(sql, values);
that's it, nothing more. This is from using:
https://node-postgres.com/
npm install pg
PART 2 - continuing on,
I think the problem might be here. This is where I am doing each
API call, then insert (that the next function call is dependent upon), some code smell here that I can't sort out.
processBatch(batch) is called, that calls the API, gets a response back, and then within there it calls `handleResponseDetail(response), where the insert is happening. I think the problem is here, if there are any ideas?
this is a code block inside:
await call_api_using_rows_from_function_above.main();
It completes with no errors, inserts rows, and commits, then the next function is called, and this next function finds no rows (inserted here). But the await on the entire main() .js blocks and waits, so I don't understand.
/**
* API call, and within call handleResponse which does the DB insert.
* #param batch
* #returns {Promise<*>}
*/
async function processBatch(batch) {
console.log('Processing batch');
return await client.send(batch).then(res => {
return handleResponseDetail(res);
}).catch(err => handleError(err));
}
// should this be async?
function handleResponseDetail(response) {
response.lookups.forEach(async function (lookup) {
if (typeof lookup.result[0] == "undefined") { // result[0] is Candidate #0
++lookup_fail;
console.log('No response from API for this address.')
} else {
++lookup_success;
const id = await insert(lookup);
}
});
}
Given the code block from your Part 2 edit, the problem is now clear: all of your insert()s are being scheduled outside of the blocking context of the rest of your async/await code! This is because of that .forEach, see this question for more details.
I've annotated your existing code to show the issue:
function handleResponseDetail(response) { //synchronous function
response.lookups.forEach(async function (lookup) { //asynchronous function
//these async functions all get scheduled simultaneously
//without waiting for the previous one to complete - that's why you can't use forEach like this
if (typeof lookup.result[0] == "undefined") { // result[0] is Candidate #0
++lookup_fail;
console.log('No response from API for this address.')
} else {
++lookup_success;
const id = await insert(lookup); //this ONLY blocks the inner async function, not the outer `handleResponseDetail`
}
});
}
Here is a fixed version of that function which should work as you expect:
async function handleResponseDetail(response) {
for(const lookup of response.lookups) {
if (typeof lookup.result[0] == "undefined") { // result[0] is Candidate #0
++lookup_fail;
console.log('No response from API for this address.')
} else {
++lookup_success;
const id = await insert(lookup); //blocks handleResponseDetail until done
}
}
}
Alternatively, if the order of insertion doesn't matter, you can use Promise.all for efficiency:
async function handleResponseDetail(response) {
await Promise.all(response.lookups.map(async lookup => {
if (typeof lookup.result[0] == "undefined") { // result[0] is Candidate #0
++lookup_fail;
console.log('No response from API for this address.')
} else {
++lookup_success;
const id = await insert(lookup);
}
})); //waits until all insertions have completed before returning
}
To reiterate, you cannot easily use .forEach() with async/await because .forEach() simply calls the given function for each element of the array synchronously, with no regard for awaiting each promise before calling the next. If you need the loop to block between each element, or to wait for all elements to complete processing before returning from the function (this is your use case), you need to use a different for loop or alternatively a Promise.all() as above.
What your main function currently does is merely creating stream, assigning listeners and instantly returning. It does not await for all the listeners to resolve like you are trying to have it do
You need to extract your file reading logic to another function, which will return a Promise that will resolve only when the entire file is read, then await for that Promise inside main
function getCsvList(csvFilePath) {
return new Promise((resolve, reject) => {
const csvList = []
fs.createReadStream(csvFilePath)
.pipe(csv())
.on('data', (data) => csvList.push(data))
.on('end', () => {
resolve(csvList)
})
.on('error', (e) => reject(e))
})
}
module.exports = {
main: async function (csvFilePath) {
try {
const csvList = await getCsvList(csvFilePath)
await insert_rows_to_db_from_csv.main(csvList);
await call_api_using_rows_from_function_above.main();
await work_with_rows_updated_in_previous_call_above.main();
await and_so_on.main();
await and_so_on_and_on.main();
await and_so_on_and_on_and_on.main();
} catch (err) {
console.log(err.stack)
} finally {
}
}
};
While running a UI-test for a login flow, the user might already be logged in. In case the user is logged in, a logout needs to be performed first.
In this case, I have to wait for either a sign in or sign out button to appear, but I don't know which will appear. I would like to simply wait for one or the other to show up, and then perform an action based on which returns first.
I simply added a silent fail on attempting to log out. If the operation times out, the script just keeps going to the log in part.
Attempting to log out and letting it fail if a timeout happens:
try {
const profileIconLocator = By.css('.frontpage-menu-item-icon-profile');
await browser.wait(Until.elementIsVisible(profileIconLocator));
const profileIcon = await browser.findElement(profileIconLocator);
assert.ok(await profileIcon.isDisplayed(), 'Ordbog profile icon button is visible');
await profileIcon.click();
const logOutLinkLocator = By.css('.frontpage-menu-dropdown-tools-item:nth-child(3)');
await browser.wait(Until.elementIsVisible(logOutLinkLocator));
const logOutLink = await browser.findElement(logOutLinkLocator);
assert.ok(await logOutLink.isDisplayed(), 'Log out link is visible');
await logOutLink.click();
} catch (error) {
console.log("LogOutStep: Failed to log out before logging in", error);
}
Then try to log in assuming that the test-user is logged out:
let signInButtonLocator = By.css('.frontpage-menu-item-icon-signin');
await browser.wait(Until.elementIsVisible(signInButtonLocator));
const signInButton = await browser.findElement(signInButtonLocator);
assert.ok(await signInButton.isDisplayed(), 'Sign in button is visible');
await signInButton.click();
I expect the user to always be logged out before logging in, but it's not ideal for the UI-test to have to wait for a timeout in the first logout attempt for every test user.
The ideal result would be that I can await for either of the two async calls to return, cancel the other upon return, and then execute a function like such:
// Wait for this to execute first(log out button)
const profileIconLocator = By.css('.frontpage-menu-item-icon-profile');
await browser.wait(Until.elementIsVisible(profileIconLocator));
// or this (sign in button)
let signInButtonLocator = By.css('.frontpage-menu-item-icon-signin');
await browser.wait(Until.elementIsVisible(signInButtonLocator));
// If sign in button shows first, run login
// If sign out button shows first, perform log out, then login again.
I may be trying to oversimplify the issue, but I'm imagining you could use a pattern like this, conditionally acting on the result of each asynchronous function only if the other asynchronous function has not yet resolved:
// Set flags
let firstPromiseResolved = false;
let secondPromiseResolved = false;
// Start asynchronous functions
firstPromise(resolve, reject).then(function(firstResult){
if(secondPromiseResolved === false){
// Maybe we want to use firstResult for some purpose here?
firstPromiseResolved = true; // Set flag so the other function won't log us out
login(); // Do the thing
}
}
secondPromise(resolve, reject).then(function(secondResult){
if(firstPromiseResolved === false){
// Maybe we want to use secondResult for some purpose here?
secondPromiseResolved = true; // Set flag so the other function won't log us in
logoutAndlogin(); // Do the other thing
}
}
I ended up solving it differently, by using a multiquery selector that waits for the first instance of one or the other CSS class. So here I wait for either the login button or log out button to appear first, and then act based on the class of the element that appeared:
const profileIconOrSignInButtonLocator = By.css('.frontpage-menu-item-icon-profile, .frontpage-menu-item-icon-signin');
await browser.wait(Until.elementIsVisible(profileIconOrSignInButtonLocator));
const loginOrLogoutButton = await browser.findElement(profileIconOrSignInButtonLocator);
var elementClass = await loginOrLogoutButton.getAttribute('class');
if (elementClass === 'frontpage-menu-item-icon frontpage-menu-item-icon-signin') {
console.log("Simply log in");
await loginFrontPage(browser);
} else { // If this happens we're already logged in, and need to log out first.
console.log("Log out and then log in");
await logoutFrontPage(browser);
await loginFrontPage(browser);
}
Please bear with me I've been dropped into a new project and trying to take it all in. I've been making progress over the last couple of day but can't seem to get over this last hump. Hopefully I can explain it correctly.
I'm loading up a web form and need to make a call out to the API to get some information that may or may not be present based on the data currently loading up. I've simplified my page to basically be this.
...Get some information the user wants and start to do some work to load
up the page and set up the form.
...old stuff working fine...
//Time for my new stuff
var testValue
async function test() {
await http.post(appConfig.serviceRootUrl + '/api/XXX/YYY',
{ mProperty: myObject.collectionInObject.itemInCollection }).then(function (result) {
if (result.length < 1) {
testValue= false;
}
else if (result[0].infoIWant.trim().length > 0) {
testValue= true;
}
});
}
test();
//Originally above in the if I was just seeing if I got a result
//and setting testValue to true/false but changed it for debugging
//and I am surely getting back correct values when the data exists
//or result.length zero when no data for it
...Do a bunch of more stuff that is old and working correctly....
//Test the new stuff up above
alert(testValue);
Most of the time I get back the correct true or false in the alert but once in a while I get back undefined. I'm guessing the undefined is because it is getting to the alert before the async/await finishes. I was under the impression it won't go past the line where I call "test();". I thought it was in effect making it halt anything below test(); until the await finished. Originally it was a bit more complex but I keep stripping it down to make it (hopefully) more basic/simple.
What am I missing in my thoughts or implementation?
Any help greatly appreciated as I'm chasing my tail at this point.
This isn't how async functions work. The function only appears to wait inside the function itself. Outside the function it is called and returns a promise synchronously.
In other words if you write:
let t = test()
t will be a promise that resolves when test() returns. In your current code, if you want to respond outside the function you would need something like:
async function test() {
let result = await http.post(appConfig.serviceRootUrl + '/api/XXX/YYY',
{ mProperty: myObject.collectionInObject.itemInCollection })
if (result.length < 1) return false
else if (result[0].infoIWant.trim().length > 0) return true;
}
// test is an async function. It returns a promise.
test().then(result => alert("result: " + result ))
Edit based on comments
Here's a working version using Axios for the http.post command:
async function test() {
let result = await axios.post('https://jsonplaceholder.typicode.com/posts',
{ mProperty: "some val" })
return result.data
}
// test is an async function. It returns a promise.
test().then(result => console.log(result ))
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
How about doing this?
...Get some information the user wants and start to do some work to load
up the page and set up the form.
...old stuff working fine...
//Time for my new stuff
var testValue
async function test() {
let promise = new Promise( (resolve, reject) => resolve( http.post(appConfig.serviceRootUrl + '/api/XXX/YYY',
{ mProperty: myObject.collectionInObject.itemInCollection }).then(function (result) {
if (result.length < 1) {
testValue= false;
}
else if (result[0].infoIWant.trim().length > 0) {
testValue= true;
}
})));
await promise;
alert(testValue);
}
test();
//Originally above in the if I was just seeing if I got a result
//and setting testValue to true/false but changed it for debugging
//and I am surely getting back correct values when the data exists
//or result.length zero when no data for it
...Do a bunch of more stuff that is old and working correctly....
//Test the new stuff up above
If you use .then() syntax, don't await, and vice versa. I was incorrect about browser compatibility with async/await, seems I haven't kept up with browser scripting, in favor of Node. But also, since you're using jQuery, $.ajax() might be a good option for you, because you don't need async/await or .then(), and you can do like so:
$.ajax(appConfig.serviceRootUrl + '/api/XXX/YYY', {
method: 'POST',
data: { mProperty: myObject.collectionInObject.itemInCollection }
}).done(function(data) {
//use result here just as you would normally within the `.then()`
})
I hope this is more helpful than my original answer.