Reputation: 625
I'm trying to learn exactly how async works in JavaScript.
In the example below I'm doing a nested database lookup inside the callback of another async database call (in the example I'm just using setTimeout
)
The way I'm trying to keep track of when I'm done going through all the products including the nested database call is by incrementing a variable called index
and checking for index == fake_data.length
.
This database library does not support Promises out of the box, so I'm really unsure what else to do. This just doesn't seem like the right way of doing it.
const fake_data = [{ type: 4 }, { type: 4 }, { type: 1 }, { type: 1 }, {
type: 4 }, { type: 3 }, { type: 2 }];
const products = [];
function test() {
setTimeout(() => {
let index = 0;
fake_data.forEach(product => {
if (products.filter(p => p.type == product.type).length == 0) {
products.push(product);
setTimeout(() => {
index++;
if (index == fake_data.length) {
test2();
}
}, 400)
}
else {
index++;
}
console.log("index", index);
})
}, 1200);
}
function test2() {
console.log(`Wooow, i have some many unique types here`);
products.forEach(p => console.log(p.type));
}
I feel like I have missed something very basic somewhere...
Upvotes: 0
Views: 75
Reputation: 33141
I'm not really sure what your question is here, but it looks as though you are trying to simulate db calls using setTimeout
, and want to just play around with promises. If that is correct, I would suggest creating separate functions that act as your db layer, so that they could be swapped out with the real thing. If you want to use setTimeout
, then wrap it in a promise too. Something like:
const fake_data = [{ type: 4 }, { type: 4 }, { type: 1 }, { type: 1 }, { type: 4 }, { type: 3 }, { type: 2 }];
// this just wraps setTimeout in a promise
const simulateWork = (time, func) => {
return new Promise(resolve => {
setTimeout(() => resolve(func()), time)
})
}
const getProducts = () => simulateWork(1200, () => fake_data)
const handleProduct = product => simulateWork(400, () => {
// do something with a product after some time
})
// the you can call things like
getProducts.then(products => {
products.forEach(handleProduct)
})
Update:
So, it looks like you want to do a bunch of async things, and then do something once it all completes. A good candidate for this is Promise.all()
, where you pass an array of async promise work, which doesn't finish until all tasks have resolved.
using the above helper functions, you could do something like the following:
// function to get unique objects
const getUnique = (arr, getVal) => {
var hash = {}
var output = []
arr.forEach(item => {
if(!hash[getVal(item)]){
hash[getVal(item)] = true
output.push(item)
}
}
return output
}
getProducts.then(products => {
// get a list of unique products and map to new work
Promise.all(getUnique(products, p => p.type).map(product => handleProduct))
// this will be called once all data completed
.then(() => test2())
}
Upvotes: 1
Reputation: 22875
You can convert your callback style code to promise based. Have a look here http://bluebirdjs.com/docs/api/promise.promisify.html
Or you can use this snippet https://gist.github.com/joelnet/ece690e942223954b1b7997ba3b1cb00#file-promisify-js
function promisify(func) {
return (...args) =>
new Promise((resolve, reject) => {
const callback = (err, data) => err ? reject(err) : resolve(data)
func.apply(this, [...args, callback])
})
}
Upvotes: 1