I am trying to run my function 3 times if refreshAppData() returns false or causes an error. However, I cannot get the function to fire again after the initial call.
How can I retry my async / await and show an error after three attempts?
async function tryRefreshAppData(retries) {
const refreshOk = await refreshAppData();
if (!refreshOk && retries > 0) {
return tryRefreshAppData(retries - 1);
}
alert("refreshAppData was unsuccessful")
throw new Error("Failed to load the data correctly");
}
const MAX_NUMBER_OF_TRIES = 3;
const RETRIES = MAX_NUMBER_OF_TRIES - 1;
await tryRefreshAppData(RETRIES);
What you have done seems to already work but you should handle the case where refreshAppData() throw an error. I put an example below :
async function refreshAppData(retries) {
if (retries === 1) {
return Promise.resolve("ok");
}
return Promise.reject("tizzi");
}
async function tryRefreshAppData(retries) {
try {
const refreshOk = await refreshAppData(retries);
if (!refreshOk && retries > 0) {
return tryRefreshAppData(retries - 1);
}
console.log("SUCCESS", refreshOk);
return refreshOk;
} catch (e) {
console.log("ERROR");
if (retries > 0) {
return tryRefreshAppData(retries - 1);
}
throw new Error("Failed to load the data correctly");
}
}
const MAX_NUMBER_OF_TRIES = 3;
const RETRIES = MAX_NUMBER_OF_TRIES;
tryRefreshAppData(RETRIES);
Here's a quick solution (written in TS tho).
you can replace resolve() with reject() in order to test it of course:
const MAX_NUMBER_OF_TRIES = 3;
const RETRIES = MAX_NUMBER_OF_TRIES - 1;
const tryRefreshAppData: (iteration: number) => Promise<void> = (iteration) => new Promise((resolve, reject) => {
setTimeout(() => {
console.log(iteration);
resolve();
}, 1000);
});
async function doAsyncCall() {
const iterations = Array.from({length: MAX_NUMBER_OF_TRIES}, (v,i) => i);
let failedAttempts = 0;
for(const iteration in iterations) {
try {
await tryRefreshAppData(parseInt(iteration));
break;
} catch {
failedAttempts +=1;
}
}
if(failedAttempts === MAX_NUMBER_OF_TRIES) {
alert("refreshAppData was unsuccessful");
}
}
doAsyncCall();
You can use the .catch() method on refreshAppData and return false:
function refreshAppData(retries) {
return new Promise((resolve, reject) => {
setTimeout( function() {
reject("Error!");
}, 750);
})
}
async function tryRefreshAppData(retries) {
console.log('retries', retries);
const refreshOk = await refreshAppData().catch(err => false);
if (!refreshOk && retries > 0) {
console.log('retry');
return tryRefreshAppData(retries - 1);
}
alert("refreshAppData was unsuccessful");
throw new Error("Failed to load the data correctly");
}
const MAX_NUMBER_OF_TRIES = 3;
const RETRIES = MAX_NUMBER_OF_TRIES - 1;
(async function() { await tryRefreshAppData(RETRIES); })()
You could also use try...catch and use the catch block to retry/show an error:
function refreshAppData(retries) {
return new Promise((resolve, reject) => {
setTimeout( function() {
reject("Error!");
}, 750);
})
}
async function tryRefreshAppData(retries) {
console.log('retries', retries);
try {
const refreshOk = await refreshAppData(retries);
} catch(err) {
if (retries > 0) {
console.log('retry');
return tryRefreshAppData(retries - 1);
}
alert("refreshAppData was unsuccessful")
throw new Error("Failed to load the data correctly");
}
}
const MAX_NUMBER_OF_TRIES = 3;
const RETRIES = MAX_NUMBER_OF_TRIES - 1;
(async function() { await tryRefreshAppData(RETRIES); })()
Related
I would like to have a cancelable promise that reports progress at the same time. Something like a combination of p-progress and p-cancelable. And while usage of any of them separately is simple I'm struggling a bit to combine them both.
This is what I tried so far which successfully reports progress but throws cancelablePromise.cancel is not a function error.
import PCancelable from 'p-cancelable';
import PProgress from 'p-progress';
const cancelablePromise = doJobWithProgress();
try {
cancelablePromise.onProgress((progress) => {
console.log('progress: ' + progress);
});
await sleep(500);
cancelablePromise.cancel();
const result = await cancelablePromise;
console.log("result", result);
}
catch (error) {
console.log("Main catch error: " + error);
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function doJobWithProgress() {
return PProgress((progressCallback) => {
return new PCancelable((resolve, reject, onCancel) => {
try {
let progress = 0;
const interval = setInterval(() => {
progress += 0.1;
progressCallback(progress);
}, 100);
const timeout = setTimeout(() => {
const result = 1;
clearTimeout(timeout);
clearInterval(interval);
resolve(result);
}, 1000);
onCancel(() => {
console.log('canceled');
clearTimeout(timeout);
clearInterval(interval);
});
}
catch (error) {
console.log('Promise catch error: ' + error);
reject(error);
}
});
});
}
To make this complete - I ended up, as #Bergi suggested, with a different approach i.e., using AbortController and extending the EventEmitter class (I'm coding in NodeJS - for the web you can inherit EventTarget).
Maybe someone will be interested in answer to the original question - when trying to combine the two libraries I ended up creating a new CancelableProgressPromise class and copy-pasting the most necessary parts from the p-progress library to it and extending the PCancelable:
import PCancelable from "p-cancelable";
export default class CancelableProgressPromise extends PCancelable {
constructor(executor) {
const setProgress = progress => {
if (progress > 1 || progress < 0) {
throw new TypeError('The progress percentage should be a number between 0 and 1');
}
(async () => {
// We wait for the next microtask tick so `super` is called before we use `this`
await Promise.resolve();
// Note: we don't really have guarantees over
// the order in which async operations are evaluated,
// so if we get an out-of-order progress, we'll just discard it.
if (progress <= this._progress) {
return;
}
this._progress = progress;
for (const listener of this._listeners) {
listener(progress);
}
})();
};
super((resolve, reject, onCancel) => {
executor(
value => {
setProgress(1);
resolve(value);
},
reject,
onCancel,
progress => {
if (progress !== 1) {
setProgress(progress);
}
},
);
});
this._listeners = new Set();
this._setProgress = setProgress;
this._progress = 0;
}
get progress() {
return this._progress;
}
onProgress(callback) {
if (typeof callback !== 'function') {
throw new TypeError(`Expected a \`Function\`, got \`${typeof callback}\``);
}
this._listeners.add(callback);
return this;
}
}
Usage example:
import CancelableProgressPromise from './cancelableProgressPromise.js';
const progressablePromise = doJobWithProgress();
try {
progressablePromise.onProgress((progress) => {
console.log('progress: ' + progress);
});
await sleep(500);
progressablePromise.cancel();
const result = await progressablePromise;
console.log("result", result);
}
catch (error) {
console.log("Main catch error: " + error);
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function doJobWithProgress() {
return new CancelableProgressPromise((resolve, reject, onCancel, progressCallback) => {
try {
let progress = 0;
const interval = setInterval(() => {
progress += 0.1;
progressCallback(progress);
}, 100);
const timeout = setTimeout(() => {
const result = 1;
clearTimeout(timeout);
clearInterval(interval);
resolve(result);
}, 1000);
onCancel(() => {
console.log('canceled');
clearTimeout(timeout);
clearInterval(interval);
});
} catch (error) {
reject(error);
}
});
}
// Output:
// progress: 0.1
// progress: 0.2
// progress: 0.30000000000000004
// progress: 0.4
// canceled
// Main catch error: CancelError: Promise was canceled
i wanna make output like this :
kembalian 1: 30000
kembalian 2: 20000
kembalian 3: 10000
kembalian 4: 5000
so after kembalian, there is number, but i wanna make it automaticly
with this code
function beliPromise(uang, harga) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
const kembalian = uang - harga;
let angka = i
for (let i = 0; i <= 10; i++) {
console.log(i)
}
console.log(`kembalian ${angka}: ${kembalian}`);
if (kembalian > 0) {
resolve(kembalian);
} else {
reject(0); // atau bisa reject('uang anda habis bro :(')
}
}, 500)
})
}
async function kembalianAwait() {
try {
const kembalian1 = await beliPromise(50000, 20000);
const kembalian2 = await beliPromise(kembalian1, 10000);
const kembalian3 = await beliPromise(kembalian2, 10000);
const kembalian4 = await beliPromise(kembalian3, 5000);
// const kembalian5 = await beliPromise(kembalian4, 50000);
// return kembalian5;
} catch (err) {
throw err
}
}
kembalianAwait()
how could be ?
You can use "await" in a for loop, for example:
function beliPromise(uang, harga) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
const kembalian = uang - harga;
if (kembalian >= 0) {
resolve(kembalian);
} else {
reject();
}
}, 500)
})
}
let uang = 50000;
async function kembalianAwait() {
const harga = [20000, 10000, 10000, 5000];
try {
for (let i = 0; i < harga.length; i++) {
let kembalian = await beliPromise(uang, harga[i])
console.log(`kembalian ${i + 1}: ${kembalian}`);
uang = kembalian
}
} catch (err) {
console.log(err);
}
}
And note the usage of >=0, I think having the exact amount of money should not result in an Error.
Use a global index variable:
var index = 1
function beliPromise(uang, harga) {
return new Promise(function(resolve, reject) {
const kembalian = uang - harga;
console.log(`kembalian ${index}: ${kembalian}`);
index ++;
if (kembalian > 0) {
resolve(kembalian);
} else {
reject(0); // atau bisa reject('uang anda habis bro :(')
}
})
}
async function kembalianAwait() {
try {
const kembalian1 = await beliPromise(50000, 20000);
const kembalian2 = await beliPromise(kembalian1, 10000);
const kembalian3 = await beliPromise(kembalian2, 10000);
const kembalian4 = await beliPromise(kembalian3, 5000);
// const kembalian5 = await beliPromise(kembalian4, 50000);
// return kembalian5;
} catch (err) {
throw err
}
}
kembalianAwait()
You have a loop, but reference i outside the loop. You need to include the loop's code into the loop:
function beliPromise(uang, harga) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
const kembalian = uang - harga;
for (let i = 0; i <= 10; i++) {
let angka = i
console.log(i)
console.log(`kembalian ${angka}: ${kembalian}`);
if (kembalian > 0) {
resolve(kembalian);
} else {
reject(0); // atau bisa reject('uang anda habis bro :(')
}
}
}, 500)
})
}
async function kembalianAwait() {
try {
const kembalian1 = await beliPromise(50000, 20000);
const kembalian2 = await beliPromise(kembalian1, 10000);
const kembalian3 = await beliPromise(kembalian2, 10000);
const kembalian4 = await beliPromise(kembalian3, 5000);
// const kembalian5 = await beliPromise(kembalian4, 50000);
// return kembalian5;
} catch (err) {
throw err
}
}
kembalianAwait()
I am unable to run the following code.
It shows me this error:
SyntaxError: await is only valid in async function
const Prom = async() => {
return new Promise((resolve, reject) => {
let a = 2;
if (a == 2) {
resolve('Its working');
} else {
reject('Its not working');
}
});
};
const final = await Prom();
console.log(final);
You could use IIFE
const Prom = async () => {
return new Promise((resolve, reject) => {
let a = 2
if (a == 2) {
resolve('Its working')
} else {
reject('Its not working')
}
})
}
;(async function() {
const final = await Prom()
console.log(final)
})()
const Prom = async () => {
return new Promise((resolve, reject) => {
let a = 2;
if (a == 2) {
resolve('Its working');
} else {
reject('Its not working');
}
});
};
const final = async () => {
const result = await Prom();
console.log(result);
};
final();
await can only be used inside an async function.
The error here is referring to the final variable. It has to be inside of an async function. Try using the below code.
const prom = new Promise((resolve, reject) => {
let a = 2;
if (a == 2) {
resolve('Its working');
} else {
reject('Its not working');
}
});
(async function() {
const final = await prom;
console.log(final)
})()
I want to test the following code:
const poll = (maxTries, interval, channel, stopTime) => {
let reached = 1;
const someInformation = someGetter();
const fetchData = async (resolve, reject) => {
const data = await networkClass.someApiCall();
if (data.stopTime === 1581516005) {
console.log("cond true");
someInformation.meta1 = transFormer(someInformation);
someInformation.meta2 = transFormer(someInformation);
someInformation.meta3 = {
...someInformation.meta1,
data,
};
resolve(someInformation);
} else if (reached < maxTries) {
reached += 1;
console.log("do it again");
setTimeout(fetchData, interval, resolve, reject);
} else {
reject(new Error('max retries reached'));
}
};
return new Promise(fetchData);
};
const checkForUpdates = () => {
setTimeout(() => {
poll(/* max retries */ 10, /* polling interval */ 1000, channel, stopTime)
.then((res) => {
setData(res);
console.log({ res: res.meta3.data });
})
.catch((e) => console.log({ e }));
}, 20000);
};
The test looks like that:
it(`should do stuff`, () => {
jest.spyOn(networkClass, 'someApiCall')
.mockResolvedValueOnce({ stopTime })
.mockResolvedValueOnce({ stopTime })
.mockResolvedValueOnce({ stopTime: 1581516005 });
checkForUpdates();
jest.advanceTimersByTime(40000);
expect(setDataMock).toHaveBeenCalled();
});
That console.log (console.log("do it again");) is only printed once, as if the test would not be able to call a setTimeout within a setTimeout. Do you have any ideas what might help?
Hello I'm new to javascript and wondering if there is a way to covnert below setInterval thingy into a promise so that .then could be used instead of the callback. Any help?
My ideas:
With a setTimeout I could resolve after a fixed time. But I'm not getting any ideas dealing with setInterval...
function alert_above(scrip, price, callback) {
var intvl = setInterval(() => {
if (get_last_price(scrip) > price) {
callback();
clearInterval(intvl);
}
}, 1000);
return intvl;
}
You can create a promise function that resolves asynchronously. Read more about Promise Here
function myInterval() {
return new Promise(resolve => {
const intId = setInterval(() => {
clearInterval(intId);
resolve();
}, 1000)
})
}
myInterval().then(() => {
console.log('Called after 1 second');
})
I think you could wrap into a new Promise like :
function promisifySetInterval(time) {
var defer = new Promise(resolve => {
let counter = 0
var intvl = setInterval(() => {
if (counter > time) {
resolve('hey');
clearInterval(intvl);
} else {
counter += 1000
}
}, 1000);
})
return defer;
}
promisifySetInterval(2000).then(param => {
console.log('hey', param)
})
And for youre case something like this :
function alert_above(scrip, price) {
var defer = new Promise(resolve => {
var intvl = setInterval(() => {
if (get_last_price(scrip) > price) {
resolve('hey');
clearInterval(intvl);
}
}, 1000);
})
return defer;
}
alert_above().then(param => {
console.log('hey', param)
})
Note: poll will be executed without delay the first time, which is different from the native setInterval.
Q: Why is poll based on setTimeout not setInterval?
A: Please see Execute the setInterval function without delay the first time.
Implementation:
// Promisify setTimeout
const pause = (ms, cb, ...args) =>
new Promise((resolve, reject) => {
setTimeout(async () => {
try {
resolve(await cb?.(...args))
} catch (error) {
reject(error)
}
}, ms)
})
// Promisify setInterval
const poll = async (interval, times, cb, ...args) => {
let result
const resolve = value => (times = 0) || (result = value)
const reject = reason => (times = 0) || (result = Promise.reject(reason))
await (async function basePoll() {
if (times > 0) {
const _result = await cb(...args, resolve, reject)
if (times) {
result = _result
--times && (await pause(interval, basePoll))
}
}
})()
return result
}
Tests:
import ordinal from 'ordinal'
// Test 1
poll(1000, 3, (a, b, c) => [a, b, c], 1, 2, 3).then(value => console.log(value))
// Test 2
let times = 0
poll(1000, 5, resolve => {
console.log(`${ordinal(++times)} time`)
times === 3 && resolve('resolved')
}).then(value => console.log(value))
// Test 3
let times = 0
poll(1000, 5, (resolve, reject) => {
console.log(`${ordinal(++times)} time`)
times === 3 && reject('rejected')
}).catch(error => console.error(error))