async function is not continuing to next line - javascript

I'm attempting to setup an async function so that my next step will not start until the function finishes.
I coded one module to connect to mongodb server, and then check to see if it's connected. These two functions work well together.
const mongoose = require('mongoose');
const mongoServer = `mongodb://127.0.0.1/my_database`;
const consoleColor = { green: '\x1b[42m%s\x1b[0m', yellow: '\x1b[43m%s\x1b[0m', red: '\x1b[41m%s\x1b[0m' }
exports.connectMongoose = () => {
mongoose.connect(mongoServer, { useNewUrlParser: true });
}
exports.checkState = () => {
const mongooseState = mongoose.STATES[mongoose.connection.readyState];
return new Promise((resolve) => {
if (mongooseState === 'connected') {
console.log(consoleColor.green, `Mongoose is ${mongooseState}.`);
resolve();
} else if (mongooseState === 'connecting') {
console.log(`Mongoose is ${mongooseState}.`);
setTimeout(() => {
this.checkState();
}, 1000);
} else {
console.log(consoleColor.red, `Mongoose is ${mongooseState}.`);
}
});
}
The next thing I tried to do was connect to the mongo db using my connectMongoose function, and then call a second function that will run my checkState function, and only perform the next function if it resolves (the if statement for the "connected" state.
const dbconfig = require('./dbconfig')
dbconfig.connectMongoose()
const testAwait = async () => {
await dbconfig.checkState();
console.log("Do this next");
}
testAwait()
The testAwait function runs, but it does not get to the console.log function which leads me to believe I'm doing something wrong when passing the resolve.

setTimeout(() => {
this.checkState();
}, 1000);
When this block is hit, the promise is never resolved. The original promise needs to resolve (as your code is currently, if the status is connecting, a new promise is created, but nothing waits for it, and the original promise never resolves). You could go with a pattern like this:
let attempts = 0;
const isConnected = async () => {
console.log("checking connection state...");
attempts++;
if (attempts >= 5) {
return true;
}
return false;
}
const wait = ms => new Promise(res => setTimeout(res, ms));
const checkState = async () => {
while (!(await isConnected())) {
await wait(1000);
}
return;
};
checkState().then(() => console.log("done"));
But to keep it more in line with what you've written, you could do:
const checkState = () => {
const mongooseState = Math.random() > 0.2 ? "connecting" : "connected";
return new Promise((resolve) => {
if (mongooseState === 'connected') {
console.log(`Mongoose is ${mongooseState}.`);
resolve();
} else if (mongooseState === 'connecting') {
console.log(`Mongoose is ${mongooseState}.`);
setTimeout(() => {
checkState().then(resolve);
}, 1000);
}
});
}
checkState().then(() => console.log("done"));

I think the issue here in the above code, you are only resolving your promise once. There is no rejection either. Thus, your code is blocked inside the promise. See the below example. You should exit the promise in any case resolve or reject.
const random = parseInt(Math.random());
const testAwait =
async() => {
await new Promise((resolve, reject) => {
if (random === 0) {
resolve(random);
} else {
reject(random);
}
});
console.log("Do this next");
}
testAwait()

Related

Repeat async function until true

I have an async function that checks for the status of an order (checkOrderStatus()). I would like to repeat this function until it returns either "FILLED" or "CANCELED", then use this return value in another function to decide to continue or stop the code. Every order goes through different status before being "FILLED" or "CANCELED", therefore the need to repeat the checkOrderStatus() function (it is an API call).
What I have now is this, to repeat the checkOrderStatus() function:
const watch = filter => {
return new Promise(callback => {
const interval = setInterval(async () => {
if (!(await filter())) return;
clearInterval(interval);
callback();
}, 1000);
});
};
const watchFill = (asset, orderId) => {
return watch(async () => {
const { status } = await checkOrderStatus(asset, orderId);
console.log(`Order status: ${status}`);
if (status === 'CANCELED') return false;
return status === 'FILLED';
});
};
I then call watchFill() from another function, where I would like to check its return value (true or false) and continue the code if true or stop it if false:
const sellOrder = async (asset, orderId) => {
try {
const orderIsFilled = await watchFill(asset, orderId);
if (orderIsFilled) {
//… Continue the code (status === 'FILLED'), calling other async functions …
}
else {
//… Stop the code
return false;
}
}
catch (err) {
console.error('Err sellIfFilled() :', err);
}
};
However, this does not work. I can see the status being updated in the terminal via the console.log in watchFill(), but it never stops and most importantly, the value in the orderIsFilled variable in sellOrder() does not get updated, whatever the value returned by watchFill() becomes.
How can I achieve the desired behavior?
watch never calls resolve (in the original code, this is misleadingly named callback()) with any value, so there's no way const orderIsFilled = await watchFill(asset, orderId); will populate orderIsFilled with anything but undefined.
If you save the result of await filter() in a variable and pass it to
callback as callback(result), your code seems like it should work.
That said, the code can be simplified by using a loop and writing a simple wait function. This way, you can return a value (more natural than figuring out how/when to call resolve), keep the new Promise pattern away from the logic and avoid dealing with setInterval and the bookkeeping that goes with that.
const wait = ms =>
new Promise(resolve => setTimeout(resolve, ms))
;
const watch = async (predicate, ms) => {
for (;; await wait(ms)) {
const result = await predicate();
if (result) {
return result;
}
}
};
/* mock the API for demonstration purposes */
const checkOrderStatus = (() => {
let calls = 0;
return async () => ({
status: ++calls === 3 ? "FILLED" : false
});
})();
const watchFill = (asset, orderId) =>
watch(async () => {
const {status} = await checkOrderStatus();
console.log(`Order status: ${status}`);
return status === "CANCELLED" ? false : status === "FILLED";
}, 1000)
;
const sellOrder = async () => {
try {
const orderIsFilled = await watchFill();
console.log("orderIsFilled:", orderIsFilled);
}
catch (err) {
console.error('Err sellIfFilled() :', err);
}
};
sellOrder();
You can use recursive functionality like this:
const checkOrderStatus = async () => {
// ... function does some work ...
await someOtherFunction() // you can use here the other async function as well
// ... function does some more work after returning from await ...
if(/* if status is FILLED or CANCELED */) {
// return true or false or some info about response for your needs
} else {
checkOrderStatus();
}
}
// this will response back when status will be FILLED or CANCELED
await checkOrderStatus();
The watch function clears the interval timer after the first call if filter resolves with false. setInterval doesn't wait for an async function to finish executing either so you'll have to create a loop yourself. Try this:
const delay = milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds));
const watch = async check => {
while (true) {
if (await check()) {
return;
}
await delay(1000);
}
};
Because watch only resolves when check succeeds, it is not possible to fail so you don't need to check for it (this might be a bug in your code):
const sellOrder = async (asset, orderId) => {
try {
await watchFill(asset, orderId);
//… Continue the code (status === 'FILLED'), calling other async functions …
}
catch (err) {
console.error('Err sellIfFilled() :', err);
}
};
p-wait-for contains an excellent implementation of this. You can use it like so:
import pWaitFor from 'p-wait-for';
const watchFill = (asset, orderId) => pWaitFor(async () => {
const { status } = await checkOrderStatus(asset, orderId);
console.log(`Order status: ${status}`);
if (status === 'CANCELED') return false;
return status === 'FILLED';
}, {
interval: 1000,
leadingCheck: false
});

How to set a JS variable depending on the duration of an asynchronous call

I have this:
this.toggleWaiting()
this.results = await this.query(term)
this.toggleWaiting()
First a loading spinner gets triggered.
Then the query runs.
And when the query function ended the loading spinner gets closed.
But what if I want to just show the loading spinner, when the query takes maybe more then 0.5 seconds?
Is there a simple way to do this?
A way to achieve this is to pass the this.query(term) promise to a function which will handle triggering the toggleWaiting only when the query takes longer than the specified amount of time (using the timeout).
For example, the below takes a promise, a function (waitingFn) which will be called with the isWaiting status as well as a timeout which you can use to specify how long you want to wait before you show the loading spinner. Finally, when the promise has been fulfilled, we return the result:
async function handleWaiting(promise, waitingFn, timeout) {
let loadingStarted = false;
let timeoutInstance = null;
const timeoutPromise = new Promise((res) => {
timeoutInstance = setTimeout(() => {
loadingStarted = true;
waitingFn(true);
}, timeout);
return res();
});
function onFinished() {
clearTimeout(timeoutInstance);
if (loadingStarted) {
waitingFn(false);
}
}
try {
const [result] = await Promise.all([promise, timeoutPromise]);
onFinished();
return result;
} catch (ex) {
onFinished();
throw ex;
}
}
You can call the handleWaiting function like so:
const result = await handleWaiting(this.query(term), (isWaiting) => this.toggleWaiting(), 500);
As #FZs and #Bergi have pointed out (thank you both), the below is an antipattern due to using the promise constructor:
function handleWaiting(promise, waitingFn, timeout) {
return new Promise((res, rej) => {
let loadingStarted = false;
const timeoutInstance = setTimeout(() => {
loadingStarted = true;
waitingFn(true);
}, timeout);
function onFinished() {
if (loadingStarted) {
waitingFn(false);
}
clearTimeout(timeoutInstance);
}
return promise
.then((result) => {
onFinished();
res(result);
})
.catch((ex) => {
onFinished();
rej(ex);
});
});
}
Thanks to ljbc1994 I found a nice solution.
Inside my alpineJs object - I have this implementation:
{
waiting: false,
async handleWaiting(promise, timeout) {
return new Promise((res, rej) => {
let loadingStarted = false;
const timeoutInstance = setTimeout(() => {
loadingStarted = true;
this.waiting = true;
}, timeout);
const onFinished = () => {
if (loadingStarted) {
this.waiting = false;
}
clearTimeout(timeoutInstance);
}
promise
.then((result) => {
onFinished();
res(result);
})
.catch((ex) => {
onFinished();
rej(ex);
});
});
},
async searchForTerm(term) {
this.results = await this.handleWaiting(this.$wire.query(term), 500);
// do something with the results...
},
}
Pretty straightforward.
For someone who is interested in the full code - here is the commit inside the github repo:
https://github.com/MichaelBrauner/sunfire-form/commit/7c1f8270e107a97b03264f5ddc5c3c3ae6f7cfd7

Resolve promise after callback it's executed

I have the following code on which I am trying to block the execution of the method _saveAddress multiple time, so I made a promise for this method.
const [pressEventDisabled, setPressEventDisabled] = useState(false);
<TouchableOpacity style={style.button_container} activeOpacity={1} disabled={pressEventDisabled} onPress={async () => {setPressEventDisabled(true); await _saveAddress(); setPressEventDisabled(false);}} >
The problem is that I want to resolve the promise after the callback method it's executed. It's there any way to wait for the dispatch function to execute or to resolve the promise inside the callback method?
This is the method for saving the address:
const _saveAddress = () => new Promise(async (resolve) => {
var valid = _validate();
if (valid) {
const address = createAddressJson();
if (addressId) {
var addressIdProperty = {
id: addressId
};
const newAddress = Object.assign(addressIdProperty, address);
dispatch(editAddress(newAddress, _onAddressSaveEditCallback));
} else {
dispatch(addAddress(address, _onAddressSaveEditCallback));
}
} else {
//notify
notifyMessage(strings.fill_required_inputs_validation);
resolve();
}
});
This is the callback method:
const _onAddressSaveEditCallback = async (success: boolean, apiValidations: any, address ? : Address, ) => {
if (success) {
if (typeof callback == 'function') {
callback(address);
}
await Navigation.pop(componentId);
} else {
setDataValidations(apiValidations);
}
};
Just do exactly what you say in the title. Nothing more, nothing less:
if (addressId) {
var addressIdProperty = {id: addressId};
const newAddress = Object.assign(addressIdProperty, address);
dispatch(editAddress(newAddress, async (s,v,a) => {
await _onAddressSaveEditCallback(s,v,a);
resolve();
}));
} else {
dispatch(addAddress(address, async (s,v,a) => {
await _onAddressSaveEditCallback(s,v,a);
resolve();
}));
}
Of course, since you are passing async () => {} to addAddress instead of _onAddressSaveEditCallback you have to call _onAddressSaveEditCallback yourself since addAddress will be calling the async () => ...
But mixing promises and callbacks like this isn't great. It leads to weird looking and sometimes confusing code. A better solution is to promisify addAddress:
function addAddressPromise (address) {
return new Promise((resolve, reject) => {
addAddress(address, (success, validations, address) {
if (success) return resolve(address);
else reject(validations)
});
});
}
Now you can wait for addAddress:
const _saveAddress = async () => {
// Don't create new promise here, we do it in addAddress..
// ...
let result = await addAddressPromise(address);
dispatch(result);
await _onAddressSaveEditCallback();
// ...
}

How to avoid this "race condition"?

I don't know if this is could be technically called a race condition...
What I have is a big baby, which can only perform one action at the time, but it's being called from an api endpoint; so simultaneous calls can occur
What I think I need to do is somehow make a queue of actions, return a promise to whoever created it, execute the actions synchronously, and resolve the promise with the value returned by the action
Here is the code (it's no real btw, just a snippet representing the problem):
class Baby {
constructor() {
this.current = 'A'
}
go(from, to) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (from === this.current) {
this.current = to
resolve()
} else {
reject(new Error('Unexpected state'))
}
}, 100)
})
}
// In order to perform the action successfully it has to do some steps in some exact order
async doAction() {
await this.go('A', 'B')
await this.go('B', 'C')
await this.go('C', 'A')
console.log('OK')
}
}
module.exports = new Baby()
And is called like this:
const baby = require('./baby')
for (let i = 0; i < 5; i++) {
doAction()
}
Thanks in advance!
Thanks to Bergi's tips, this is the final solution:
Basically, this keeps a chain of promises and when a new action needs to be added it's chained to the current chain of promises
const baby = {
current: 'A',
action: Promise.resolve(),
go(from, to) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (from === this.current) {
this.current = to
resolve()
} else {
reject(new Error('Unexpected state'))
}
}, 100)
})
},
doAction() {
this.action = this.action.then(async () => {
await this.go('A', 'B')
await this.go('B', 'C')
await this.go('C', 'A')
console.log('OK')
})
}
}
baby.doAction()
baby.doAction()
baby.doAction()

Javascript async function basic

First of all I am new to async functions. Where is the problem in this code? I just get test 1 and test 2 output.
let isAvailable = false;
console.log('test1');
let asynchFunc = () => {
return new Promise(() => {
if (isAvailable === true) {
console.log('here asyncFunc');
}
});
}
(async () => {
await asynchFunc();
});
console.log('test2');
isAvailable = true;
I don't know if i say it in a better way:
If you add a timeout inside the promise, isAvailable = true instruction will get executed before reaching the check on this variable, otherwise, isAvailable will remain false.
You can test both cases below
Without timeout
let isAvailable = false;
console.log('test1');
let asynchFunc = () => {
return new Promise((resolve, reject) => {
if (isAvailable === true) {
console.log('here asyncFunc');
resolve('has been available');
} else {
reject('still not available')
}
});
}
(async () => {
try {
let res = await asynchFunc();
console.log(res);
} catch (e) {
console.log(e);
}
})();
console.log('test2');
isAvailable = true;
With Timeout
let isAvailable = false;
console.log('test1');
let asynchFunc = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (isAvailable === true) {
console.log('here asyncFunc');
resolve('has been available');
} else {
reject('still not available')
}
}, 2000);
});
}
(async () => {
try {
let res = await asynchFunc();
console.log(res);
} catch (e) {
console.log(e);
}
})();
console.log('test2');
isAvailable = true;
Did you mean to call the async function like this?
(async () => {
await asynchFunc();
})();
(Note the added () at the end)
I'm not sure what you're trying to test here, the async function will still see the false value. You can see that by changing your function to:
let asynchFunc = () => {
return new Promise(() => {
console.log('check isavailable:', isAvailable);
});
}
If you're just looking for a setup that will let you play around with async functions then you can use this code:
const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time));
let isAvailable = false;
(async () => {
console.log('isAvailable:', isAvailable);
await sleep(1000);
console.log('isAvailable:', isAvailable);
})();
console.log('test2');
isAvailable = true;
Just be aware that using a sleep function like this to wait for something to be available is completely the wrong way of doing things. You should never have an isAvailable flag, the value should just be a promise that will resolve once it's available. What language are you coming from? What is your actual issue? We can help you better with real world problems.
Here's a function you can play around with that might help you understand what's going on a bit more. Note you're using a immediately invoked function expression (IIFE) to switch from synchronous to asynchronous code, which is fine as long as you understand that console.log('test2') in your original code may not process in the same order as your code looks like it might. However, you did not call the IIFE, so simply add () to the end of it.
I.e.,
(function (){})();
(()=>{})();
(async function(){})();
(async ()=>{})();
As for the rest of your code, you don't resolve or reject your promise, return the promise correctly from the function, and you need to try{}catch{} your asynchronous function otherwise reject() from the promise will be unhandled.
(function(){
let isAvailable = false;
let asynchFunc = () => new Promise((resolve,reject) => {
if (isAvailable === true) {
console.log('Available');
resolve(true);
}else{
console.log('Not available');
reject(false)
}
});
(async () => {
try{
// isAvailable = true; // test with me.
console.log('test1');
// result is always true, unless there is an error.
// but if there is an error
// the rejected promise will go to catch(e){}.
let result = await asynchFunc();
console.log('test2');
isAvailable = true;
}catch(e){
console.log('Error:',e)
}
})();
})();
let isAvailable = false;
console.log('test1');
let asyncFunc = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (isAvailable === true) {
resolve('here asyncFunc');
}
}, 2000);
});
}
(async() => {
return await asyncFunc(); // await used here is optional
})().then(v => {
console.log(v); // prints value after 2 seconds.
});
console.log('test2');
isAvailable = true;
All Promises need to be either resolved or rejected. In your case you did neither. In order to see the value in the async function, I passed the value to the resolve callback.
In an async function it is optional to use the await key when returning a value as the return value of an async function is implicitly wrapped in Promise.resolve.

Categories

Resources