在javascript中以异步方式在循环中执行函数 - 在哪里放置defer.resolve?

时间:2015-09-04 09:29:28

标签: javascript node.js asynchronous promise sails.js

我来自java / python背景,是javascript的新手。我需要创建一个产品列表,其中包含其子项的描述以及jsonarray。

parent_list:

[{ children: [ 100714813, 100712694 ],
  sp: '89.10',
  weight: '1 ltr',
  pack_type: 'Carton',
  brand: 'Real',
  p_desc: 'Fruit Power Juice - Orange' }]

现在,对于每个父母,我需要通过连接到数据库再次迭代地获取子细节,最后将结果合并到一个jsonarray中。但是当我执行下面的代码时,控件不会等待获取子数据(这是异步调用它有意义!),我得到的结果是一个jsonarray,它只包含没有父数据的数据儿童。

exports.productDetailsQuery = function(options) {

    var AEROSPIKE_NAMESPACE = '';  
    var AEROSPIKE_SET = 'products';
    var PD_KEY_VERSION_NUMBER = '1';

    var defer = sails.Q.defer();

    var results = options.results;
    var parent_list = [];
    var finalData = [];

    var productKeys = results.map(
        function(x){
            return {
                ns: AEROSPIKE_NAMESPACE,
                set: AEROSPIKE_SET,
                key: "pd.v" + PD_KEY_VERSION_NUMBER + '.' + 'c' + options.city_id + '.' + x.sku.toString()
            }
        }
    );

    var status = require('aerospike').status;
    var breakException = {};

    // Read the batch of products.
    sails.aerospike.batchGet(productKeys, function (err, results) {
        if (err.code === status.AEROSPIKE_OK) {
            for (var i = 0; i < results.length; i++) {
                switch (results[i].status) {
                    case status.AEROSPIKE_OK:
                        parent_list.push(results[i].record);
                        break;
                    case status.AEROSPIKE_ERR_RECORD_NOT_FOUND:
                        console.log("NOT_FOUND - ", results[i].keys);
                        break;
                    default:
                        console.log("ERR - %d - ", results[i].status, results[i].keys);
                }
            }
            parent_list.forEach(function(parent){
                var children = parent['children'];
                console.log(children)
                if(children){
                    var childKeys = children.map(function(child){
                        return {
                            ns: AEROSPIKE_NAMESPACE,
                            set: AEROSPIKE_SET,
                            key: "pd.v" + PD_KEY_VERSION_NUMBER + '.' + 'c' + options.city_id + '.' + child.toString()
                        }
                    });
                    sails.aerospike.batchGet(childKeys, function(err, childData){
                        if(err.code === status.AEROSPIKE_OK){
                            console.log('this called')
                            var entry = {};
                            entry['primary_prod'] = parent;
                            entry['variants'] = childData;
                            finalData.push(entry);
                        }
                    });
                }
                else{
                    var entry = {};
                    entry['primary_prod'] = parent;
                    finalData.push(entry);
                }
            });
            defer.resolve(finalData);
        } else {
            defer.reject(err);
        }
    });

    return defer.promise;
}

我需要finalData像:

[{"primary_prod":{ children: [ 100714813, 100712694 ],
  sp: '89.10',
  weight: '1 ltr',
  pack_type: 'Carton',
  brand: 'Real',
  p_desc: 'Fruit Power Juice - Orange' },
"variants":[{child_data},{child_data}]}, ...........]

非常感谢有关如何使其发挥作用的任何帮助。是否有特定模式来处理此类情况?

谢谢!

2 个答案:

答案 0 :(得分:1)

您所写的内容是沿着正确的方向行,但只有外部puts((*iter))才会被宣传。因为没有尝试宣传内部batchGet(),所以它对最终返回的承诺没有贡献。

您的整体模式可能是这样的......

batchGet()

...其中exports.productDetailsQuery = function(options) { return sails.aerospike.batchGetAsync(...).then(results) { var promises = results.filter(function(res) { // Filter out any results that are not `AEROSPIKE_OK` ... }).map(function(parent) { // Map the filtered results to an array of promises return sails.aerospike.batchGetAsync(...).then(function(childData) { ... }); }); // Aggregate the array of promises into a single promise that will resolve when all the individual promises resolve, or will reject if any one of the individual promises rejects. return sails.Q.all(promises); }); } batchGetAsync()的宣传版。

完全充实的代码将会更长,但可以通过首先定义几个实用程序函数来保持相当简洁和可读性。你最终会得到这样的东西:

batchGet()

答案 1 :(得分:1)

使用新的ES6加异步功能和babel更简单。您可以npm i -g babel npm i babel-runtime然后使用babel test.js --optional runtime --stage 2 | node编译并运行以下内容:

import {inspect} from 'util';                                   

let testData = [                                                
  { id: 0, childIds: [1,2]},                                    
  { id: 1, childIds:[] },                                       
  { id: 2, childIds:[] }                                        
];                                                              

function dbGet(ids) {                                           
  return new Promise( r=> {                                     
    r(ids.map((id) => { return testData[id];}));                
  });                                                           
}                                                               

async function getChildren(par) {                               
  let children = await dbGet(par.childIds);                     
  par.children = children;                                      
}                                                               

async function getAll(parentIds) {                              
  let parents = await dbGet(parentIds);                         
  for (let p of parents) {                                      
    await getChildren(p);                                       
  }                                                             
  return parents;                                               
}                                                               

async function test() {                                         
  var results = await getAll([0]);                              
  console.log(inspect(results,{depth:3}));                      
}                                                               

test().then(f=>{}).catch( e=> {console.log('e',e)});