在javascript中异步执行循环中的函数-在何处放置defer.resolve?
我来自java/python背景,对javascript还不熟悉。我需要创建一个产品列表,并在jsonarray中包含其子产品的描述 家长名单:在javascript中异步执行循环中的函数-在何处放置defer.resolve?,javascript,node.js,asynchronous,promise,sails.js,Javascript,Node.js,Asynchronous,Promise,Sails.js,我来自java/python背景,对javascript还不熟悉。我需要创建一个产品列表,并在jsonarray中包含其子产品的描述 家长名单: [{ children: [ 100714813, 100712694 ], sp: '89.10', weight: '1 ltr', pack_type: 'Carton', brand: 'Real', p_desc: 'Fruit Power Juice - Orange' }] 现在,对于每个父级,我需要通过连接到数据库
[{ 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;
}
如果您能为我们提供帮助,我们将不胜感激。是否有专门的模式来处理此类案件
谢谢 您所写的内容是正确的,但只有外部的
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()
的预期版本
完全充实的代码将更长,但可以通过首先定义两个实用函数来保持合理的简洁性和可读性。你可能会得到这样的结果:
// utility function for making a "key" object
function makeKey(obj) {
return {
ns: '', //AEROSPIKE_NAMESPACE
set: 'products', //AEROSPIKE_SET
key: 'pd.v1.c' + options.city_id + '.' + obj.toString()
}
}
// promisified version of batchGet()
function batchGetAsync(obj) {
var defer = sails.Q.defer();
batchGet(obj, function(err, results) {
if(err.code === status.AEROSPIKE_OK) {
defer.resolve(results);
} else {
defer.reject(err);
}
});
return defer.promise;
}
var status = require('aerospike').status;
// Main routine
exports.productDetailsQuery = function(options) {
return batchGetAsync(options.results.map(makeKey)).then(results) {
var promises = results.filter(function(res) {
if (res.status === status.AEROSPIKE_OK) {
return true;
} else if(status.AEROSPIKE_ERR_RECORD_NOT_FOUND) {
console.log("NOT_FOUND - ", res.keys);
} else {
console.log("ERR - %d - ", res.status, res.keys);
}
return false;
}).map(function(parent) {
var entry = { 'primary_prod': parent },
children = parent['children'];
if(children) {
return batchGetAsync(children.map(makeKey)).then(function(childData) {
entry.variants = childData;
return entry;
});
} else {
return entry;
}
});
return sails.Q.all(promises);
});
}
有了新的ES6 plus异步功能和babel,就更简单了。您可以
npm i-g babel
npm i babel运行时
然后使用babel test.js——可选运行时——第2阶段节点
编译并运行以下内容:
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)});
基本上finallyData只有来自if(children){}的else{}块的值。如何确保在if(children){}中所有正在运行的进程完成之前不返回finalData。代码的哪些部分实际上是异步的?您应该为每个异步任务创建一个自己的延迟/承诺,并将延迟的任务尽可能靠近调用。非常感谢!非常感谢你的帮助!感谢您花时间回答问题-非常感谢!
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)});