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.
Related
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.
Suppose I have two promises. Normally, the code cannot execute until all these promise are finished. In my code, suppose I have promise1 and promise 2
then I have await promise1 and await promise2. My code won't execute until both these promise are finished. However what I need is that if one of these two is finished then the code can execute and then we can ignore the other one.
That is possible because the code only needs one of these awaits to succeed. Is this possible to implement in javascript (nodejs) ?
Promise.any is what you're looking for:
Promise.any() takes an iterable of Promise objects. It returns a single promise that resolves as soon as any of the promises in the iterable fulfills, with the value of the fulfilled promise. If no promises in the iterable fulfill (if all of the given promises are rejected), then the returned promise is rejected with an AggregateError, a new subclass of Error that groups together individual errors.
Example:
const randomDelay = (num) => new Promise((resolve) => setTimeout(() => {
console.log(`${num} is done now`);
resolve(`Random Delay ${num} has finished`);
}, Math.random() * 1000 * 10));
(async() => {
console.log("Starting...");
const result = await Promise.any(new Array(5).fill(0).map((_, index) => randomDelay(index)));
console.log(result);
console.log("Continuing...");
})();
You can use Promise.any() function. You can pass an array of Promises as an input, and it will resolve as soon as first Promise resolves.
(async() => {
let promises = [];
promises.push(new Promise((resolve) => setTimeout(resolve, 100, 'quick')))
promises.push(new Promise((resolve) => setTimeout(resolve, 500, 'slow')))
const result = await Promise.any(promises);
console.log(result)
})()
Promise.race() or Promise.any()
If one of the promises's status changed whatever rejected or fulfilled, you can use Promise.race()
If you want one of the promises to be fulfilled, you should use
Promise.any()
Promise.any can continue to execute other code if any One Promise will resolved.
let i = 1;
const newPromise = () => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`promise${i++} resolved`);
resolve(true);
}, Math.random() * 1000)
});
}
(async() => {
await Promise.any([newPromise(), newPromise()]);
console.log("Code will executes if any promise resolved");
})()
I just wanted to know if it is considered good practice to nest promises like in this example, or is there better alternatives ?
getDatabaseModel.searchId(idNoms).then(function([noms, elements]) {
getDatabaseModel.getLocalisations().then(function(localisations){
getDatabaseModel.getStates().then(function(states){
//Some code
})
})
})
Obviously, your promises are independent. So you should use Promise.all() to make it run parallel with the highest performance.
The Promise.all() method takes an iterable of promises as an input,
and returns a single Promise that resolves to an array of the results
of the input promises
var searchById = getDatabaseModel.searchId(idNoms);
var getLocalisations = getDatabaseModel.getLocalisations();
var getStates = getDatabaseModel.getStates();
var result = Promise.all([searchById, getLocalisations, getStates]);
result.then((values) => {
console.log(values);
});
For example, Let's say each promise takes 1s - So it should be 3s in total, But with Promise.all, actually it just takes 1s in total.
var tick = Date.now();
const log = (v) => console.log(`${v} \n Elapsed: ${Date.now() - tick}`);
log("Staring... ");
var fetchData = (name, ms) => new Promise(resolve => setTimeout(() => resolve(name), ms));
var result = Promise.all(
[
fetchData("searchById", 1000),
fetchData("getLocalisations", 1000),
fetchData("getStates", 1000)
]);
result.then((values) => {
log("Complete...");
console.log(values);
});
Besides, If you're concern about asyn/await with more elegant/concise/read it like sync code, then await keyword much says the code should wait until the async request is finished and then afterward it'll execute the next thing. While those promises are independent. So promise.all is better than in your case.
var tick = Date.now();
const log = (v) => console.log(`${v} \n Elapsed: ${Date.now() - tick}`);
var fetchData = (name, ms) => new Promise(resolve => setTimeout(() => resolve(name), ms));
Run();
async function Run(){
log("Staring... ");
var a = await fetchData("searchById", 1000);
var b = await fetchData("getLocalisations", 1000);
var c = await fetchData("getStates", 1000);
log("Complete...");
console.log([a, b, c]);
}
Promises were made to avoid callback hell, but they are not too good at it too. People like promises until they find async/await. The exact same code can be re-written in async/await as
async getModel(idNoms){
const [noms, elements] = await getDatabaseModel.searchId(idNoms);
const localisations = await getDatabaseModel.getLocalisations();
const state = await getDatabaseModel.getStates():
// do something using localisations & state, it'll work
}
getModel(idNoms);
Learn async/await here
IMO it's a little hard to read and understand. Compare with this:
getDatabaseModel.searchId(idNoms)
.then(([noms, elements]) => getDatabaseModel.getLocalisations())
.then(localization => getDatabaseModel.getStates());
As #deceze pointed out there are two things to note:
These functions are called serially
They don't seem to depend on each other as the noms, elements and localization are not used at all.
With Promise.all you can mix and match however you want:
// Call `searchId` and `getState` at the same time
// Call `getLocalisations` after `searchId` is done
// wait for all to finish
Promise.all([
getDatabaseModel.searchId(idNoms).then(([noms, elements]) => getDatabaseModel.getLocalisations()),
getDatabaseModel.getStates()
]).then(([result1, result2]) => console.log('done'));
// Call all 3 at the same time
// wait for all to finish
Promise.all([
getDatabaseModel.searchId(idNoms),
getDatabaseModel.getLocalisations(),
getDatabaseModel.getStates(),
]).then(([result1, result2, result3]) => console.log('done'));
I'm fairly new to NodeJs and understanding the concept of async/await. Please correct me if I'm wrong - await keyword blocks the code until it gets a resolved value. For example:
const sampleFunction = async () => {
const result = await someAsynFunctionReturningPromise();
console.log('Log is printed here!');
}
In the above code, the compiler stops the code at 'const result = await someAsynFunctionReturningPromise();' until 'someAsynFunctionReturningPromise()' gets resolved, right?
With the above assumption, I tried the below setTimeout() code:
const sampleFunction = async () => {
const result = await setTimeout(()=>{
console.log('This is printed after 2 seconds');
},2000);
console.log('Log is printed here!');
}
However, the above code doesnt await till setTimeout() is resolved, it skips to the next line, printing 'Log is printed here!'.
Can anyone please help me understand if my understanding is wrong?
In order for await to work with setTimeout, setTimeout needs to return a promise. Please see this: How to make a promise from setTimeout
This is where the event loop of node JS working comes in. In the first place your
someAsynFunctionReturningPromise()
is returning a promise where in the second code setTimeout() does not return you promise. Also when nodejs eventloop gets such a function like setTimeout() it will put it in callstack and then to taskqueue where it will wait for 2000ms and goes for the next line of execution, that is why it is printing
console.log('Log is printed here!');
when 2000ms are over the setTimeout will be kept from taskqueue to callstack and it will execute
console.log('This is printed after 2 seconds');
You wrote your own answer. You wrote
await keyword blocks the code until it gets a resolved value
In your code you never resolved anything so of course it won't work
A setTimeout that resolves is
function wait(ms = 0) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, ms);
};
}
or the terse version
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
Example
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
const sampleFunction = async () => {
for(;;) {
await wait(2000);
console.log('time since page loaded:', performance.now() / 1000 | 0);
}
};
sampleFunction();
I have three promises. I need to combine them together to create one more promise.
Something like this Promise.all('url1','url2','url3') or if there is other possible way to achieve it.
Intent is to fire all of them in parallel.
I have requirement that when either two of them are resolved, then the final one will be resolved.
How I can achieve it?
The following might give you an idea. You have the chance to run the first resolving promise and the last one. But the logic is straightforward and you may play as you wish. I haven't included the error handling rejection logic but that should be fairly easy.
The bring() function returns a promise resolving in 100~200ms.
var bring = n => new Promise(v => setTimeout(v,~~(Math.random()*100)+100,`from url_${n}`)),
pcnt = 0,
proms = Array.from({length: 10})
.map((_,i) => bring(i).then(v => ++pcnt === 1 ? (console.log(`Do stg with the 1st resolving promise ${v} immediately`), [pcnt,v])
: pcnt < proms.length ? console.log(`Do nothing with intermediate resolving promise ${v}`)
: (console.log(`Do stg with the last resolving promise ${v} finally\n`), [pcnt,v])
)
);
Promise.all(proms)
.then(vs => console.log("Or when all completes, deal with the values returned from the first and last promise\n",vs.filter(d => !!d)));
.as-console-wrapper {
max-height: 100% !important
}
So you want to come up with a promise that resolves when, for example, 2 of 3 promises resolve. Promise.all wouldn't be the right thing to use because, well, you aren't waiting for all the promises to resolve. You'd have to do it manually:
const resolveAfterSeconds = function(sec) {
return new Promise(res => {
setTimeout(res, sec * 1000)
})
}
new Promise((resolve) => {
const promises = [
resolveAfterSeconds(1),
resolveAfterSeconds(2),
resolveAfterSeconds(3),
];
let resolveCount = 0;
promises.forEach(prom => {
prom.then(() => {
console.log('resolving');
resolveCount++;
if (resolveCount === promises.length - 1) resolve();
});
});
}).then(() => console.log('done'));