0

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...

3
  • This isn't about your main question, but are you sure about products.filter(p => p.type == product.type).length == 0) This will be true only when filter finds nothing, but you've set it up so the filter always finds something. Commented May 14, 2018 at 21:58
  • @Mark_M I'm pretty sure that it will only return true if there's no product with the same type in products. Console.logs: New type 4 Already exists New type 1 Already exists Already exists New type 3 New type 2. Commented May 14, 2018 at 22:04
  • My mistake @jones — I thought products was your fake_data. Commented May 14, 2018 at 22:05

2 Answers 2

1

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])
  })
}
Sign up to request clarification or add additional context in comments.

4 Comments

So the solution is to add a 3rd party package? Hmm.
@jones Updated my answer
Cool thanks, I'm having trouble applying it to the example however but this looks to be the solution
1

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())
}

2 Comments

The overall idea is to make a nested async database call to fetch additional data and save it for unique products. Since it's async, I'm using index to prevent calling test2() too early before getting all the data if that makes sense.
Ok, I think you need promise.all. I have updated my answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.