0

I am trying to return an array of pushed data from promise, then loop multiple times to populate all data. For instance, I got a list of brands and a function to take in brand parameter and return an array of pushed data.

var datasetarr = [];
brandlist = ['Bh', 'Ruffles'];

let promiseKey = new Promise((resolve, reject) => {
    for(var i = 0; i < brandlist.length; i++){
        datasetarr = populateData(brandlist[i], datasetarr);
    }
    resolve(datasetarr);
});

promiseKey.then((arr) => {
    console.log('complete promise');
    for(var i = 0; i < arr.length; i++){
        console.log(arr[i].date + ' ' + arr[i].total);
    }
});

The error message is

Uncaught (in promise) TypeError: Cannot read property 'length' of undefined
    at promiseKey.then

My data fetching has no problem as I managed to print out the details. This means that the promise is not resolve properly. Is that the correct way to return an array from promise? I not sure which part was wrong.

QWERTY
  • 2,303
  • 9
  • 44
  • 85
  • `promise` is undefined – guest271314 Aug 16 '17 at 01:50
  • yup, i didn't find promise either. – tibetty Aug 16 '17 at 01:53
  • note, `let promiseKey = ...` will resolve, almost immediately to `undefined` because `populateData` doesn't return anything – Jaromanda X Aug 16 '17 at 01:55
  • @JaromandaX _"I'm surprised at the error you are getting though"_ See original post. `promise` was not defined. After the edit `datasetarr` is still undefined at `.then()` – guest271314 Aug 16 '17 at 01:58
  • no, I see why `datasetarr` is `undefined`, because `populateData` returns `undefined` - and `populateData` would fail the second time around, because `datasetarr.push` will fail as `dataserarr` argument would be `undefined` second time around – Jaromanda X Aug 16 '17 at 01:59
  • @JaromandaX Have you read the original post? – guest271314 Aug 16 '17 at 02:00
  • @guest271314 - yes, I can read code, thanks for your concern :p – Jaromanda X Aug 16 '17 at 02:00
  • Did you meant to assign `datasetarr[i] = …` in that loop? – Bergi Aug 16 '17 at 02:03
  • @Bergi When I callt he populateData(), it should return an array filtered by the brand parameter. Then, I wanted to add that array returned to the datasetarr and loop until the end of brandlist. – QWERTY Aug 16 '17 at 02:06
  • @DeniseTan First of all you should realise that [you cannot return an array](https://stackoverflow.com/q/14220321/1048572?how-to-return-the-response-from-an-asynchronous-call) from `populateData`, but a promise for it at best. – Bergi Aug 16 '17 at 02:08
  • @Bergi Sorry but how do I access the array from the returned promise? – QWERTY Aug 16 '17 at 02:11
  • @JaromandaX Is there any way to return an array from promise or accessing returned array from returned promise? – QWERTY Aug 16 '17 at 02:14
  • @DeniseTan First of all, you never even returned the promise :-) See my answer for how I'd do it – Bergi Aug 16 '17 at 02:18

2 Answers2

3

Firstly, your populateData needs to return a Promise - in this case, it would be the resolved array of promises created in data.forEach

var brandlist = ['Bh', 'Ruffles'];

let promiseKey = Promise.all(brandlist.map(brand => populateData(brand)))
.then(results => [].concat(...results)); // flatten the array of arrays

promiseKey.then((arr) => {
    console.log('complete promise');
    for(var i = 0; i < arr.length; i++){
        console.log(arr[i].date + ' ' + arr[i].total);
    }
});

function populateData(brand, datasetarr) {
    console.log('go in');
    var query;// = // query by brand parameter
    return query.once('value').then(data => {
        var promises = [];
        data.forEach(snapshot => {
            // get each receipt item details

            // get receipt details by receipt ID
            var query;// = // query receipts
            promises.push(query.once('value').then(data => { 
                // removed code
                // managed to print out here so data fetching is not a problem
                console.log(brand + ' ' + date + ' ' + itemTotal);
                return {date: date, total: itemTotal};
            })); 
        }); 
        return Promise.all(promises);
    });
}

Or, using the snapshotToArray function I gave you in this answer a week ago

const snapshotToArray = snapshot => {
    const ret = [];
    snapshot.forEach(childSnapshot => {
        ret.push(childSnapshot);
    });
    return ret;
};

function populateData(brand, datasetarr) {
    console.log('go in');
    var query;// = // query by brand parameter
    return query.once('value').then(data => Promise.all(snapshotToArray(data).map(snapshot => {
        // get each receipt item details

        // get receipt details by receipt ID
        var query;// = // query receipts
        return query.once('value').then(data => { 
            // removed code
            // managed to print out here so data fetching is not a problem
            console.log(brand + ' ' + date + ' ' + itemTotal);
            return {date: date, total: itemTotal};
        }); 
    }))); 
}
Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
  • 2
    no, you didn't misinterpret anything. This user asked that more complex question a week ago - I would've hoped they'd use some of the "techniques" in the answer, but clearly they haven't, because this code suffers almost the same lack of understanding as the code last week – Jaromanda X Aug 16 '17 at 02:49
  • last week there were some conditionals, which resulted in `undefined` array entries, so there was this nasty filtering being done :p I say nasty, because I think I overused `.filter` to be honest – Jaromanda X Aug 16 '17 at 02:50
2

While it's possible to work with a global datasetarray variable to which you push from anywhere, I would recommend against it. Instead, write a method getData that returns (a promise for) an array, and after calling that multiple times (once for every brand) you concatenate them together.

const brandlist = ['Bh', 'Ruffles'];
const promiseKey = Promise.all(brandlist.map(getData)).then(arrays => [].concat(...arrays));
promiseKey.then(arr => {
    console.log('complete promise');
    for (const item of arr)
        console.log(item.date + ' ' + item.total);
});

function getData(brand) { // no array parameter!
    console.log('go in');
    const query = …; // query by brand parameter
    return query.once('value').then(data => {
        const promises = toArray(data).map(snapshot => {
        const query = …; // get receipt item details by receipt ID
        return query.once('value').then(data => { 
            …
            return {date: date, total: itemTotal}; // don't push, just return the result
        });
        return Promise.all(promises); // resolves with an array of results
    }); // resolves with that same result array
}
function toArray(forEachable) {
    const arr = [];
    forEachable.forEach(x => { arr.push(x); });
    return arr;
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375