I wrote a function to get a random number after 500 milliseconds and it worked perfectly with (return new Promise (resolve,reject)), also, it worked without it by using setTimeout() function, but when I added the promise and setTimeout() inside it wont show the result, I have console.log() before and after the result and both are showing. Please note that i am still new to the promise concept in general since this is a task from my coding school, thank you.
My code:
function getRandomNumber () {
new Promise ((resolve,reject) =>{
console.log('Getting Random Number...');
let number = Math.random();
setTimeout( () => {
if(number){
resolve (`OK ${number}`);
console.log('Done!');
}
else
reject ('ERROR');
}, 500);
});
}
getRandomNumber();
Result:
First, return your promise, second use the result the promise provides either by creating a variable from it and awaiting it or using then
function getRandomNumber() {
return new Promise((resolve, reject) => {
console.log('Getting Random Number...');
let number = Math.random();
setTimeout(() => {
if (number) {
resolve(`OK ${number}`);
console.log('Done!');
} else {
reject('ERROR');
}
}, 500);
});
}
getRandomNumber().then(result => console.log(result));
(async () => {
const number = await getRandomNumber();
console.log(number);
})();
Another option is to write sleep as a separate reusable function. Note async function can await multiple values and return values of their own. Calling an async function will always return a Promise -
function sleep(time) {
return new Promise(resolve => setTimeout(resolve, time))
}
async function getRandomNumber() {
console.log('Getting Random Number...')
await sleep(500)
const number = Math.random()
console.log("Done!")
return number
}
async function main() {
const number1 = await getRandomNumber()
console.log("number1", number1)
const number2 = await getRandomNumber()
console.log("number1", number2)
const number3 = await getRandomNumber()
console.log("number1", number3)
return number1 + number2 + number3
}
main().then(console.log, console.error)
.as-console-wrapper { min-height: 100%; }
Getting Random Number...
Done!
number1 0.2907058138817884
Getting Random Number...
Done!
number1 0.05784624607512423
Getting Random Number...
Done!
number1 0.889702848981964
1.2382549089388766
You need to return the promise then you can use await inside async function to get the result
function getRandomNumber () {
return new Promise ((resolve,reject) =>{
console.log('Getting Random Number...');
let number = Math.random();
setTimeout( () => {
if(number){
resolve (`OK ${number}`);
console.log('Done!');
}
else
reject ('ERROR');
}, 500);
});
}
const printNumber = async () => console.log(await getRandomNumber());
printNumber();
Related
I am trying to write an async function that is going to await a function that got passed as one parameter and I wanted this async function to retry this operation for 5 mins every 10 seconds.
I found one function that sort of does this but it retries based on the numbers of times instead.
async function retry(fn, n) {
for (let i = 0; i < n; i++) {
try {
const ret = await fn();
if(!ret) throw new Error() // if `ret` is null or undefined, we will retry.
return ret
} catch {}
}
throw new Error(`Failed retrying ${n} times`);
}
Is there a way to tweak this function to satisfy my use cases?
Since your function is async, you can easily create timeouts to wait between subsequent calls:
const sleep = t => new Promise(r => setTimeout(r, t))
async function retry(fn) {
const startTime = Date.now()
while(Date.now() - startTime < 5 * 60 * 1000) {
try {
const ret = await fn();
if(ret)
return ret
// if `ret` is null or undefined, we won't return.
} catch {}
await sleep(10 * 1000)
}
throw new Error(`Failed retrying`);
}
Trying to create a function retry that returns a function that calls and returns value from callback function passing its arguments and catches errors. If error is caught it should return the callback function with catch. If number of errors exceeds count then throw an Error.
Here is what was done so far:
const retry = (count, callback) => {
let attempts = 1;
const _retry = async (...args) => callback(...args)
.catch(err => {
if (attempts > count) throw err
attempts++
return _retry(...args)
});
return _retry
}
The problem appears when called:
var r = Math.random().toString(36).slice(2)
var arg = (n) => async (...v) =>
--n < 0 ? v : Promise.reject(Error(`>>> x:${v}`))
await retry(3, arg(2))(r)
It looks to me like retry returns a Promise right now, due to the async keyword. Try dropping the async keyword from the retry definition and make the _retry function async:
const retry = (count, callback) => {
let attempts = 1;
return _retry = async (...args) => callback(...args)
.catch(err => {
if (attempts > count) throw err
attempts++
return _retry(...args)
});
}
I'm requesting to server "S" to get some data, but this data may not be ready.
When the data is not yet ready, server S responds with {"data":null,"state": "pending"} but when the data has been prepared the response will be something like {"data": {...somedata}, "state": "done"}.
I have to repeat the request until the data is ready. What I'm doing now is something like this:
let wait = function* () {
let t = 500;
for (let j = 1; j < 10; j++) {
yield new Promise((resolve) => {
setTimeout(() => resolve(), t*=2);
});
}
}();
let result = await sendRequestToS();
status = result;
for (let i = 0; i < 4 && result.state==='pending'; i++) {
await wait.next().value;
result = await sendRequestToS();
}
As you can see, I send the request up to 4 times with a delay of 1, 2, 4 and 8 seconds.
Am I doing this the right way?
Isn't that (using setTimeout to delay between requests) a bad practice?
I'd write this as such:
function wait(ms) {
return new Promise(res => setTimeout(res, ms));
}
async function requestAndRetry() {
let retries = 10;
let timeout = 1000;
while(retries>0) {
const response = await sendRequestToS();
if (result?.state === 'done') {
return result;
}
await wait(timeout);
retries--;
timeout*=2;
}
throw new Error('Request failed after 10 retries');
}
I don't think it's a bad idea. It's called exponential back-off and you're not blocking the script engine.
Instead of using generators directly, you could simply do this using async/await and recursion. Here's an example which tries to get the response a limited number of times in order to prevent an endless recursion and with a timeout between retries:
async function wait(timeInMs) {
console.log('Waiting ...');
return new Promise((resolve => setTimeout(() => resolve(), timeInMs)));
}
async function tryRequestToS(numberOfTries, timeout) {
if (numberOfTries <= 0) {
throw new Error("could net get result");
}
const result = await sendRequestToS();
if (result && result.state === "done") {
return result;
}
await wait(timeout); // wait for the defined timeout before recurring
return tryRequestToS(numberOfTries - 1, timeout);
}
(async () => {
try {
const result = await tryRequestToS(10, 500); // max. 10 retries, 500 ms delay between retries
console.log(result);
} catch(err) {
console.log(err);
}
})();
What are the advantages using the chain with '.then' if it does not work like a expected:
new Promise(function(resolve, reject) {
// A mock async action using setTimeout
setTimeout(function() { resolve(10); }, 3000);
})
.then(function(num) {
console.log('first then: ', num); return num * 2; })
.then(function(num) {
setTimeout(function() {
console.log('second then: ', num); return num * 2; }, 500);
})
.then(function(num) {
console.log('last then: ', num);
});
// RESULT!
// From the console:
// first then: 10
// last then: undefined
// second then: 20
I was expecting for the following result:
// RESULT!
// From the console:
// first then: 10
// second then: 20
// last then: 40
The second then has to return another promise if you want the third then to fire after the timeout in the second then.
This version of your code will give you the desired result:
new Promise(function (resolve) {
setTimeout(function() {
resolve(10);
}, 3000);
})
.then(function (num) {
console.log('first then: ', num); return num * 2;
})
.then(function (num) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log('second then: ', num);
resolve(num * 2);
}, 500);
});
})
.then(function (num) {
console.log('last then: ', num);
});
The reason why your code didn't work as expected was that the third then was invoked immediately after the second timeout started, with the result of the call to setTimeout, which is undefined.
I looks like you expected the result you are returning from the callback of the second timeout to be somehow passed as result of the second then, but that's not the way it works.
So my thinking was right, when recursing with promises, we end up calling all chained callbacks for however many times we recurse, for example
function p() {
return new Promise(function (r) {
process.nextTick(r);
})
}
function recurse(count) {
return p().then(function () {
if (count < 10) {
console.log('count => ', count);
return recurse(++count);
}
}).then(function(){
console.log('a');
return 5;
});
}
recurse(1).then(function () {
console.log('done');
});
If you run the above, we get:
count => 1
count => 2
count => 3
count => 4
count => 5
count => 6
count => 7
count => 8
count => 9
a
a
a
a
a
a
a
a
a
a
done
Is there a way to register this callback with console.log('a') just once instead of registering it 10 times?
I don't think I want/need this function to be called 10 times, and would like to find a way to have it called just once.
I am actually just as interested in a similar solution for Observables, specifically RxJS5 Observables.
I guess the only solution is to nest the remainder of your code inside the first promise callback like so:
function p() {
return new Promise(function (r) {
process.nextTick(r);
})
}
function recurse(count) {
return p().then(function () {
if (count < 10) {
return recurse(++count);
} else {
// all your remaining code would have to go here
console.log('a');
return 5; // or return someOtherPromise() or anything
}
});
}
recurse(1).then(function () {
console.log('done');
});
If the recursion is synchronous, you can simply recurse within the .then's function
new Promise(res => {
res(); // dummy initial promise
}).then(() => {
function recurse(x) { // recursion inside then
console.log('x', x);
if (x < 10) return recurse(++x);
return x;
}
return recurse(1); // begin recursion
}).then(y => { // this only fires once recursion above is resolved
console.log('y', y);
return 5;
}).then(z => console.log('z', z));
// x 1
// ... (synchronous)
// x 10
// y 10 (value passed from resolving inner promise)
// z 5 (value returned from previous then)
Or if our recursive function is asynchronous, we can have it return a promise too, so you end up with a recursion which looks like this
function doWork() {
function addOne(x) {
return new Promise((res, rej) => {
// async bit here
res(x + 1);
});
}
function recurse(x) {
if (x < 10) return addOne(x).then(recurse);
return x;
}
return recurse(1);
}
And in a promise chain, would look like this
new Promise(res => {
res(); // dummy initial promise
}).then(() => {
return doWork();
}).then(y => {
console.log(y);
});