未捕获(承诺)TypeError:无法读取属性'长度'在promiseKey.then中未定义

时间:2017-08-16 01:46:18

标签: javascript asynchronous promise resolve

我正在尝试从promise返回推送数据的数组,然后多次循环以填充所有数据。例如,我得到了一个品牌列表和一个接受品牌参数的功能,并返回一系列推送数据。

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

错误消息是

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

我的数据提取没有问题,因为我设法打印出详细信息。这意味着承诺未得到妥善解决。这是从promise中返回数组的正确方法吗?我不确定哪一部分是错的。

2 个答案:

答案 0 :(得分:3)

首先,你的populateData需要返回一个Promise - 在这种情况下,它将是在data.forEach中创建的已解析的promise数组

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

或者,使用我在this answer a week ago

中提供的snapshotToArray函数
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};
        }); 
    }))); 
}

答案 1 :(得分:2)

虽然可以使用datasetarray来自任何地方push的全局getData变量,但我建议不要这样做。相反,编写一个返回(承诺)数组的方法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; } ,并在多次调用之后(每个品牌一次)将它们连接在一起。

fileprivate func pauseLayer(layer: CALayer) {
    let pausedTime = layer.convertTime(CACurrentMediaTime(), from: nil)
    layer.speed = 0.0
    layer.timeOffset = pausedTime
}

fileprivate func resumeLayer(layer: CALayer) {
    let pausedTime = layer.timeOffset
    layer.speed = 1.0
    layer.timeOffset = 0.0
    layer.beginTime = 0.0
    let timeSincePause = layer.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
    layer.beginTime = timeSincePause
}