Wait until promise returns true - javascript

I have a database setup with NodeJS and want to wait until certain table is created before start to create any others. This method tableExists resolves with the status being either true/false, but I want it to wait until it's true only.
const checkTableExists = async () => {
const exists = await queryInterface.tableExists('Subjects');
return exists;
}
How can I force a wait until checkTableExists returns true?

Using setTimeout:
const CHECK_INTERVAL = 200; // every 200ms
const checkTableExists = async () => {
const exists = await queryInterface.tableExists('Subjects');
if (!exists) {
return new Promise((resolve, reject) => {
setTimeout(() => checkTableExists().then(resolve).catch(reject), CHECK_INTERVAL);
});
}
return exists;
}
The solution to something like this is not to keep on waiting. There are other issues that may cause the table not to be created. You may want to adjust the above code to stop checking after it has checked for set number of times, or a duration has passed. Use something reasonable, depending on the environment where your db is running.

Add a delay and repeat:
// Utility function
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const checkTableExists = async () => {
while (true) {
const exists = await queryInterface.tableExists('Subjects');
if (exists) return true;
await delay(10000); // Wait 10 seconds before trying again.
}
}
Although this resolves the promise with true, it is actually is not necessary to return a boolean, as the resolving promise is enough as a signal that the table now exists -- true is the only possible outcome when the promise resolves.

Related

How can I do I cancel javascript await sleep?

The most common implementation of a sleep function in javascript is returning a Promise after setTimeout resolves:
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
I have for loop with await sleep to keep it from executing too fast, such as not requesting xhr too fast. I also have a isBreak flag elsewhere to tell me when to stop the for loop. However, the issue I have is that when I break the for loop, the previous await sleep has already executed and is holding up the for loop. Is there a better way of breaking the for loop and also terminating the await sleep instantaneously?
const items = [];
let isBreak = false; // Somewhere else in the application
for (const item of items) {
if (isBreak) break;
// Do something, like xhr request
await sleep(15000); // 15 seconds sleep
if (isBreak) break;
}
Is there a way for me to signal for early
In JS, when an await operation starts, it can no longer be interrupted; it will wait until its operand promise is settled.
So, you have to make the promise you're awaiting cancelable in some way.
Unfortunately, your code can't get notified about a variable reassignment (when you set isBreak to true), and polling it would be inefficient.
Instead of a flag, you could use an AbortSignal (which was invented for this purpose), and make your sleep accept one:
function sleep(ms, signal) {
return new Promise((resolve, reject) => {
signal.throwIfAborted();
const timeout = setTimeout(() => {
resolve();
signal.removeEventListener('abort', abort);
}, ms);
const abort = () => {
clearTimeout(timeout);
reject(signal.reason);
}
signal.addEventListener('abort', abort);
});
}
Then, you use it like this:
const items = [];
const isBreak = new AbortController(); // Somewhere else in the application, call `isBreak.abort()`
try {
for (const item of items) {
// Do something, like xhr request
await sleep(15000, isBreak.signal); // 15 seconds sleep
}
} catch (e) {
if (e.name === 'TimeoutError') {
// Handle a cancellation
console.log('Cancelled');
} else {
// Not a cancellation, rethrow it
throw e;
}
}
An AbortSignal works well with fetch as well, in case you have to cancel that too.
An answer i found in a blog in the past that i adjusted. It is similiar to FZs answer. Same usage, too. Just to give an alternative.
relevant too: How to cancel timeout inside of Javascript Promise?
function sleep(ms, abortSignal) {
return new Promise((resolve, reject) => {
signal.addEventListener("abort", abort);
if(abortSignal.aborted){
abort();
}
const timeout = setTimeout(end, ms);
function abort() {
clearTimeout(timeout);
abortSignal.removeEventListener("abort", abort);
reject(new Error("sleep aborted"));
}
function end() {
abortSignal.removeEventListener("abort", abort);
resolve();
}
});
}

retrieve logs from server while performing a "parallel" promise.all( request to the server) (parallel challenge!)

Arriving with a theory question :)
I have a front that sends (axios) N requests in a Promise.all() with a map function. This works fine. Each time one of the promises is good, I have a little table that gets updated with each request's answer until I get the full table and the array of the answers at the end. ✅
The problem comes when I want to read, at the same time, the logs of the server
So my objective is to run another axios request to my express.js server that will run each 2 seconds to retrieve the logs of the last 2 seconds, this way I could show the logs of what is happening with each answer in real time.
Any ideas of how doing this two tasks in parallel?
In the front I'm using react and the promise.All has this is structure:
setIsLoading(true); // setting a flag to know this is running
const doAllTheTable = await Promise.all(
tableData.map(async (lineOfMyTable) => {
const answer = await doMyRequest(lineOfMyTable) // my axios.get request
return updateTableLine(answer) // the functions that update the good line
})
);
//all promises are good now
setIsLoading(false)
So, basically I want to have another loop that runs each 2 seconds while "isLoading" is true to update another part of my front and show the logs meanwhile. But I need both things to happen at the same time!
Thank you for your ideas :)
Rather than awaiting your Promise.all immediately, store a reference to the promise so you can start checking the logs:
const doAllTheTablePromise = Promise.all(
tableData.map(async lineOfMyTable => {
const answer = await doMyRequest(lineOfMyTable); // my axios.get request
return updateTableLine(answer); // the functions that update the good line
});
);
let cancelled = false;
(async () => {
while (!cancelled) {
// Check your logs..
await new Promise(r => setTimeout(r, 2000)); // 2 second delay
}
})();
await doAllTheTablePromise;
cancelled = true;
Once your doAllTheTablePromise has resolved, you can stop checking the logs.
Must be many ways to write this. Here's one involving a token provided by the caller of two async processes, foo() and bar(), for communication between them.
async function foo(tableData, token) {
try {
await Promise.all(tableData.map(async (lineOfMyTable) => {
const answer = await doMyRequest(lineOfMyTable);
return updateTableLine(answer);
}));
token.setIsLoading = false; // lower flag when all requests are complete
} catch(error) {
token.setIsLoading = false; // lower flag if synchronous or asynchronous error occurs
}
}
async function bar(token) {
function delay(ms) { // this can be written as inner or outer function, whichever suits.
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
if(token.setIsLoading) {
let logs = await retrieveLogs();
// process/display logs here
await delay(2000);
return bar(token); // recursive call
} else {
return "complete"; // optional
}
}
async function myCaller() {
// ... preamble
let loadingToken = { // passed to foo() and bar() as a means of communication between them.
'setIsLoading': true // raise flag before calling foo() and bar().
};
return Promise.all(foo(tableData, loadingToken), bar(loadingToken));
}
EDIT:
Maybe better written like this, with the caller looking after lowering the flag:
async function foo(tableData) {
return Promise.all(tableData.map(async (lineOfMyTable) => {
return updateTableLine(await doMyRequest(lineOfMyTable));
}));
}
async function bar(token) {
function delay(ms) { // this can be written as inner or outer function, whichever suits.
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
if(token.setIsLoading) {
let logs = await retrieveLogs();
// process/display logs here
await delay(2000);
return bar(token); // recursive call
} else {
return "complete"; // optional
}
}
async function myCaller() {
// ... preamble
let loadingToken = { // passed to bar().
'setIsLoading': true // raise flag before calling foo() and bar().
};
return Promise.all(
foo(tableData).finally(() => { loadingToken.setIsLoading = false }),
bar(loadingToken)
);
}

Promise resolved too fast

I am using Promises and in the meanwhile i have a loading animation.
the problme is that my promise is resolved fast and the loader is quickly disappear.
So i want to launch a promise and if the promise is resolved before 3 sec wait the remaining time.
Example
export const operation = () => {
const a = new Date();
const myPromise = doAction().then(() => {
const b = new Date();
if((b - a) < 3000)
operationIsDone();
else
setTimeout(() => {operationIsDone();}, b - a)
});
}
Is there any npm or a better way doing it?
Thanks in advance.
It is much easier to use a second promise that just runs the minimum waiting time. Then use Promise.all to wait for both to finish.
That way, your script will always wait at least the default delay but also longer if yourOwnPromise takes longer than that.
const wait = delay => new Promise(resolve => setTimeout(resolve, delay));
const doAction = () => wait(500); // TODO replace this with your own function
const yourOwnPromise = doAction();
yourOwnPromise.then(() => {
console.log('yourOwnPromise resolved now');
});
Promise.all([yourOwnPromise, wait(3000)]).then(() => {
console.log('both resolved now');
});
See Promise.all for details.

While Loop inside ASYNC AWAIT

I have some code that continuously updates a series of objects via network calls looks like this. I was wondering if this is bad practice and if there might be a better way. I cant use Set Interval as the time between MakeAsyncCall replies is variable and can cause a leak if the time to make the call is longer than the delay. I will be using this info to update a UI. Will this cause blocking? What are your thoughts? Let me know if you need more info.
let group = [item1, item2, item3];
// Start Loop
readForever(group, 100);
// Function to Delay X ms
const delay = ms => {
return new Promise((resolve, _) => {
const timeout = setTimeout(() => {
resolve();
}, ms);
});
};
// Function to continuously Make Calls
const readForever = async (group, ms) => {
while(true) {
// Make Async Call
for (let item of group) {
await MakeAsyncCall(item);
}
// Wait X ms Before Processing Continues
await delay(ms);
}
};
The given code won't cause any UI blocking. And is a valid way to update the UI continually.
Instead of a loop you could write it that way:
const readForever = async (group, ms) => {
// Make Async Call
for (let item of group) {
await MakeAsyncCall(item);
}
// Wait X ms Before Processing Continues
await delay(ms);
if (true) { // not needed, but there you could define an end condition
return readForever(group, ms);
}
};
In addition to the comment about the delay function:
You could directly pass the resolve to setTimeout, and because you do not cancel the Timeout anywhere you do not need to store the result setTimeout in a variable.
const delay = ms => {
return new Promise(resolve => setTimeout(resolve, ms));
};

If I await 2 functions can I guarantee that the return object will have both values

I have the following code
module.exports = async function (req, res) {
const station1 = await getStation('one')
const station2 = await getStation('two')
return { stations: [station1, station2] }
}
Can I be guaranteed that when the final return value is sent it will definitely have both station1 and station2 data in them, or do I need to wrap the function call in a Promise.all()
As you have it, it is guaranteed that the return statement will only be executed when the two getStation() promises have resolved.
However, the second call to getStation will only happen when the first promise has resolved, making them run in serial. As there is no dependency between them, you could gain performance, if you would run them in parallel.
Although this can be achieved with Promise.all, you can achieve the same by first retrieving the two promises and only then performing the await on them:
module.exports = async function (req, res) {
const promise1 = getStation('one');
const promise2 = getStation('two');
return { stations: [await promise1, await promise2] }
}
Now both calls will be performed at the "same" time, and it will be just the return statement that will be pending for both promises to resolve. This is also illustrated in MDN's "simple example".
The await keyword actually makes you "wait" on the line of code, while running an async action.
That means that you don't proceed to the next line of code until the async action is resolved. This is good if your code has a dependency with the result.
Example:
const res1 = await doSomething();
if(res1.isValid)
{
console.log('do something with res1 result');
}
The following code example will await a promise that gets resolved after three seconds. Check the date prints to the console to understand what await does:
async function f1() {
console.log(new Date());
// Resolve after three seconds
var p = new Promise(resolve => {
setTimeout(() => resolve({}),3000);
});
await p;
console.log(new Date());
}
f1();
ES6Console
BTW, In your case, since you don't use the result of station1 it's better using Promise.all to work parallel.
Check this example (it will run for 3 seconds instead of 4 seconds the way you coded above):
async function f1() {
console.log(new Date());
// Resolve after three seconds
var p1 = new Promise(resolve => {
setTimeout(() => resolve({a:1}),3000);
});
// Resolve after one second
var p2 = new Promise(resolve => {
setTimeout(() => resolve({a:2}),1000);
});
// Run them parallel - Max(three seconds, one second) -> three seconds.
var res = await Promise.all([p1,p2]);
console.log(new Date());
console.log('result:' + res);
}
f1();
ES6Console.
If either of await getStation('one') or await getStation('two') fails an exception will be thrown from the async function. So you should always get the resolved value from both promises.
You can rewrite your function as follows to use Promise.all
module.exports = async function (req, res) {
try{
const [station1, station2] = await Promise.all([
getStation('one'),
getStation('two')
]);
return { stations: [station1, station2] };
} catch (e) {
throw e;
}
}

Categories

Resources