i have a utility function that check for items in indexeddb and invalidated them
invalidateCache() {
let now = Date.now()
return keys(cacheStore).then((keys) => { // 1st
keys.map((key) => {
return this.getCachedResponse(key).then((item) => { // 2nd
if (item.expire < now) {
this.deleteCache(key)
}
})
})
}).catch((err) => {
console.error(err)
})
}
now i need to make sure that the 2nd promise has resolved before i chain into another function, like so
this.invalidateCache().then(() => { // 2nd promise has finished its work
// check the db
this.getCachedResponse()
.then((res) => {
if (res) {} // item is not expired
else {} // make http request
but unfortunately the this.invalidateCache().then(() resolve to the 1st promise not the nested.
so how can i continue chaining after the nested 2nd promise ?
You need to use Promise.all to wait for all promises:
return keys(cacheStore).then((keys) => { // 1st
return Promise.all(keys.map((key) => {
return this.getCachedResponse(key).then((item) => { // 2nd
if (item.expire < now) {
return this.deleteCache(key)
}
})
}));
})
This is slightly more readable using async / await:
async invalidateCache() {
const now = Date.now()
const keys = await getKeys(cacheStore);
await Promise.all(keys.map(key => this.expireKey(now, key)));
}
async expireKey(time, key) {
const item = await this.getCachedResponse(key);
if(item.expire < time)
this.deleteCache(key);
}
Related
I have the following structure of code:
var cdict = new Map();
fetch("randomurl")
.then(res => res.json())
.then(data => {
for (const obj of data.result) {
// insert stuff into Map cdict
}
counter(0).then(res => {
console.log(cdict);
})
// ^ here after calling of counter i need to do stuff
});
const cbegin = 0;
const ccount = 10;
function counter(cnt) {
if (cnt < ccount) {
setTimeout( () => {
cnt++;
fetch(someurl)
.then(res => res.json())
.then(data => {
for (const obj of data.result) {
// do stuff
}
})
.catch(err => {
console.log(err);
});
counter(cnt);
}, 1000);
}
}
Here after execution of counter(0) call and all its fetch requests, I wish to execute a line console.log(cdict);
How can this be achieved? And is this proper way to call fetch requests with delay of 1 second?
Don't mix setTimeout event queue callbacks with promise-based code -
const sleep = time =>
new Promise(resolve => setTimeout(resolve, time))
async function main()
{ console.log("please wait 5 seconds...")
await sleep(5000)
console.log("thanks for waiting")
return "done"
}
main().then(console.log, console.error)
Don't write .then(res => res.json()) every time you need some JSON. Write it once and reuse it -
const fetchJSON(url, options = {}) =>
fetch(url, options).then(res => res.json())
async function main()
{ const data = await fetchJSON("path/to/data.json")
console.log("data received", data)
return ...
}
main().then(console.log, console.error)
Don't attempt to declare variables outside of Promises and modify them later. You cannot return the result of an asynchronous call. Asynchronous data needs to stay contained within the promise or you will be chasing hard-to-find bugs in your code -
async function main(urls)
{ const result = []
for (const u of urls) // for each url,
{ result.push(await getJSON(u)) // await fetch and append to result
sleep(1000) // wait 1 second
}
return result
}
const myUrls =
[ "foo/path/data.json"
, "another/something.json"
, "and/more/here.json"
]
main(urls)
.then(result => /* counter logic */)
.then(console.log, console.error)
Continue abstracting as you see fit -
// give reusable functions a name; use parameters for configurable behaviour
async function fetchAll(urls, delay = 100)
{ const result = []
for (const u of urls)
{ result.push(await getJSON(u))
sleep(delay)
}
return result
}
async function counter(urls)
{ const results = await fetchAll(urls) // use our generic fetchAll
const cdict = /* counter logic... */
return cdict
}
const myUrls =
[ "foo/path/data.json"
, "another/something.json"
, "and/more/here.json"
]
counter(urls).then(console.log, console.error)
As you can see async and await prevent nesting that occurs with the use of setTimeout or .then callbacks. If you use them correctly, your code remains flat and you can think about your code in a synchronous way.
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
});
I'm making a program that takes an array of links and returns how many are broken and how many are working. Right now, I'm testing it with an array that has four working links and two broken links. Here's my code:
function getBrokenLinks(linksArr){
let links = linksArr
let brokenLinks = 0
links.forEach(link => {
fetch(link.href)
.then( res => {
if ( res.status != 200 ){
brokenLinks++
}
}).then( () => {console.log(brokenLinks)})
})
return brokenLinks
}
and this is the output i receive:
output
I want the console to print the total of broken links only once, and after it has completed fetching all the links.
You need to wait for all promises first. Then you can print the result. Also, to return anything, you need to make the function async and then all your outer code must also be async!
async function getBrokenLinks (linksArr) {
let brokenLinks = 0
await Promise.all(linksArr.map(link => (async () => {
try {
const res = await fetch(link.href)
if (res.status != 200) brokenLinks++
} catch (e) {
brokenLinks++
}
})()))
console.log(brokenLinks)
return brokenLinks
}
You can use Promise.all to wait for all promises to be resvoled:
/*
Promise.all([promise1, promise2,..])
.then(function() {
// all promises have been resolved
})
*/
function getBrokenLinks(linksArr) {
let links = linksArr
let brokenLinks = 0
let promises = []
links.forEach(link => {
// save promise to push onto array
let promise = fetch(link.href)
.then(res => {
if (res.status != 200) {
brokenLinks++
}
})
promises.push(promise)
})
return Promise.all(promises)
.then(() => {
return brokenLinks
})
}
// Calling code:
/*
getBrokenLinks([])
.then(console.log)
*/
Is there any difference in the way these two chains are handled or are they handled in the same way? Is there any benefit to using one over the other?
I've tried both calls and they both return the same result (32) -- and my assumption is that they process in the same manner but I have a friend who is telling me they work differently.
const getNewDataPromise = num => new Promise( (resolve, reject) => {
typeof num === 'number' ? resolve(num * 2) :
reject(`${num} is not a number -- input must be a numeric value.`);
});
getNewDataPromise(2).then( data => {
const nowEight = getNewDataPromise(data);
return nowEight;
}).then( data => {
const nowSixteen = getNewDataPromise(data);
return nowSixteen;
}).then( data => {
const nowThirtyTwo = getNewDataPromise(data);
return nowThirtyTwo
}).then( data => {
console.log(data);
}).catch( err => {
console.log(err);
});
getNewDataPromise(2).then( data => {
return getNewDataPromise(data);
}).then( data => {
return getNewDataPromise(data);
}).then( data => {
return getNewDataPromise(data);
}).then( data => {
console.log(data);
}).catch( err => {
console.log(err);
});
There is no difference at all between your two versions in terms of outcome. The first one just creates an intermediate local variable which doesn't affect the outcome or parallelism or anything like your friend has asserted.
Your second one is more concise and just as clear and would be my preference between your two.
Another option would be to use async/await which is particularly useful for sequences of asynchronous operations:
async function run() {
try {
let data = await getNewDataPromise(2);
data = await getNewDataPromise(data);
data = await getNewDataPromise(data);
data = await getNewDataPromise(data);
console.log(data);
} catch(e) {
console.log(e);
}
}
Or, if you were really just calling the same function over and over, you could use a loop too which would be a bit less repetitive (more DRY):
async function run() {
try {
let data = 2;
for (let i = 0; i < 4; i++) {
data = await getNewDataPromise(data);
}
console.log(data);
} catch(e) {
console.log(e);
}
}
The requirement is finishing the current function before moving to the next call:
var current_data = something;
Run(current_data).then((data1) => {
Run(data1).then(data2 => {
Run(data2).then(data3 => {
// and so on
})
})
});
The example above is only possible if I know exactly how much data I want to get.
In order to make the nested promises part of promise chain, you need to return the nested promises.
Run(current_data).then((data1) => {
return Run(data1).then(data2 => {
return Run(data2).then .....
});
});
I'm gonna assume your data is paginated and you don't know how many pages there are, therefore you can use a while loop with await inside of an async function like so:
(async function() {
var currentData = someInitialData;
// loop will break after you've processed all the data
while (currentData.hasMoreData()) {
// get next bunch of data & set it as current
currentData = await Run(currentData);
// do some processing here or whatever
}
})();
You can use the async-await to make code more readable.
async function getData(current_data){
let data1 = await Run(current_data)
let data2 = await Run(data1);
let result = await Run(data2);
return result;
}
Calling the getData function
getData(data)
.then(response => console.log(response))
.catch(error => console.log(error));
Try to avoid nested promises. If you need to call a series of promises, which depend on the previous call's response, then you should instead chain then like the following following -
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('foo');
}, 1000);
});
promise1.then((response) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(response + ' b');
}, 1000);
});
}).then((responseB) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(responseB + ' c');
}, 1000);
});
}).then((responseC) => {
console.log(responseC); // 'foo b c'
})
if your code can support async-await then what Mohammed Ashfaq suggested is an alternative.
If you are executing the same function over and over again but on different data, I would make a recursive function that returns return a Promise.
I just look at my example below using an an array of numbers, you can edit it to your current case.
var current_data = [1,2,4,5,6]
function Run(data){
if(data.length === 0)
return Promise.resolve(data);
return new Promise((resolve, reject)=>{
//your async operation
//wait one second before resolving
setTimeout(()=>{
data.pop()
console.log(data)
resolve(data)
},1000)
})
.then((results)=>{
return Run(results)
})
}
Run(current_data)