我认为这是一个非常愚蠢的问题,但我很难绕过承诺。
我正在使用Q(用于nodejs)来同步几个异步函数。 这就像一个魅力。
var first = function () {
var d = Q.defer();
fs.readdir(path,function(err,files){
if(err) console.log(err);
d.resolve(files);
});
return d.promise;
};
var second = function (files) {
var list = new Array;
files.forEach(function(value, index){
var d = Q.defer();
console.log('looking for item in db', value);
db.query(
'SELECT * FROM test WHERE local_name =? ', [value],{
local_name : String,
},
function(rows) {
if (typeof rows !== 'undefined' && rows.length > 0){
console.log('found item!', rows[0].local_name);
d.resolve(rows[0]);
} else {
var itemRequest = value;
getItemData(itemRequest);
}
}
);
list.push(d.promise);
});
return Q.all(list);
};
first()
.then(second)
.done(function(list){
res.send(list);
});
我遇到的问题是这个小功能:
getItemData(itemRequest)
这个函数充满了几个回调。 promise链运行正常,但忽略了我使用的所有回调(例如我在函数中进行的几次XHR调用)。
该函数的简化版本看起来像这样(只是为了给你一个想法):
function getItemData(itemRequest){
helper.xhrCall("call", function(response) {
var requestResponse = JSON.parse(response)
, requestInitialDetails = requestResponse.results[0];
downloadCache(requestInitialDetails,function(image) {
image = localImageDir+requestInitialDetails.image;
helper.xhrCall("call2", function(response) {
writeData(item,image,type, function(){
loadData(item);
});
});
} else {
writeData(item,image,type, function(){
loadData(item);
});
}
});
});
我使用的xhr函数如下所示:
xhrCall: function (url,callback) {
var request = require("request")
, colors = require('colors');
request({
url: url,
headers: {"Accept": "application/json"},
method: "GET"
}, function (error, response, body) {
if(!error){
callback(body);
}else{
console.log('Helper: XHR Error',error .red);
}
});
}
所以我的问题:
再次,对不起,如果这是一个非常愚蠢的问题,但我不知道这里有正确的行动方案。
谢谢!
[编辑] Q.nfcall,我不明白
所以我一直在研究Q.nfcall,它允许我使用节点回调。我只是不明白这是如何工作的。 有人可以给出一个简单的例子,我将如何将它用于具有多个异步xhr调用的函数?
我试过了,但你可以看到我真的不明白我在做什么:
var second = Q.nfcall(second);
function second (files) {
[编辑2]
这是我的getitemdata函数回调链中的最终函数。此函数基本上与函数'second'相同,但我直接推送结果然后返回promise。这可以按照说明工作,但没有所有额外的回调数据,因为它不会等待回调与任何数据一起返回。
function loadData(item) {
var d = Q.defer();
db.query(
'SELECT * FROM test WHERE local_name =? ', [item],{
local_name : String,
},
function(rows) {
if (typeof rows !== 'undefined' && rows.length > 0){
list.push(d.promise);
}
}
);
});
return Q.all(list);
};
答案 0 :(得分:1)
第二次修改后你的回答并不是很清楚。
首先,在您的原始问题上,您的getItemData
对承诺链没有任何影响
您可以更改功能的呼叫签名并传递您的延期承诺。
getItemData(itemRequest, d)
并将此延期承诺一直传递给您的xhrCall
并在那里解决。
我会重新编写整个实现,并确保所有函数都返回promises。
许多人认为延期承诺是一种反模式。所以我使用了和谐定义的Promise
API(下一个javascript)
在说完之后,我会重新实现你原来的代码(我还没有测试过)
var Promise = Promise || require('es6-promise').Promise // a polyfill
;
function errHandler (err){
throw err
}
function makeQuery () {
var queryStr = 'SELECT * FROM test WHERE local_name =? '
, queryOpt = {local_name: String}
;
console.log('looking for item in db', value)
return new Promise(function(resolve, reject){
db.query(queryStr, [value], queryOpt, function(rows) {
if (typeof rows !== 'undefined' && rows.length > 0){
console.log('found item!', rows[0].local_name);
resolve(rows[0]);
} else {
// note that it returns a promise now.
getItemData(value).then(resolve).catch(errHandler)
}
})
})
}
function first () {
return new Promise(function(resolve, reject){
fs.readdir(path, function(err, files){
if (err) return reject(err)
resolve(files)
})
})
}
function second (files) {
return Promise.all(files.map(function(value){
return makeQuery(value)
});
}
first()
.then(second)
.then(res.send)
.catch(errHandler)
请注意,done
API上没有Promise
方法。
新Promise
API的一个缺点是错误处理。看看bluebird
它是一个健壮的promise库,它与新的promise API兼容,并具有许多Q
辅助函数。
答案 1 :(得分:0)
据我所知,您需要从getItemData
返回承诺。像在Q.defer()
中一样使用second()
,并在回调完成数据时解决它。然后,您可以将其推送到list
。
要保存代码,可以使用Q.nfcall立即调用节点样式回调函数,并返回一个promise。请参阅API文档中的示例:https://github.com/kriskowal/q/wiki/API-Reference#qnfcallfunc-args