我有一个从三个数组中创建数据库对象的函数。数组在每个循环中填充,其中一个数组依赖于循环的相同迭代中的值。
依赖数组使用requests库和cheerio库来获取字符串以填充数组。
目前依赖数组填充空值,我认为是因为循环不等待返回请求。
我还在学习,并希望在没有直接阻止的情况下让它工作以保持异步,所以我正在研究promises / callbacks。
这是在服务器端完成的,但是从我在cheerios docs中看到的,没有承诺能力。
这是我到目前为止所拥有的。 (getFile()是不填充'c'数组的函数,它还取决于放入'b'的当前值。我知道getFile函数通过控制台日志测试获得正确的值,因此问题必须在填充'c'的实现中。
addToDB()是一个将值保存到mongoDB中的函数,从测试我知道对象被正确放入db,只是c数组不正确。
function getInfo(path) {
$(path).each(function(i,e) {
a.push(...)
b.push(value)
c.push(getFile(value))
})
var entry = new DB...//(a,b,c)
addToDB(entry);
}
function getFile(...) {
request(fullUrl, function (err, resp, page) {
if (!err && resp.statusCode == 200) {
var $ = cheerio.load(page); // load the page
srcEp = $(this).attr("src");
return srcEp;
} // end error and status code
}); // end request
}
我一直在阅读承诺/回调然后()但我还没有发现任何有用的东西。
答案 0 :(得分:0)
首先,您必须了解这样一个事实,即至少部分依赖于异步子流程的任何流程本身就是异步的。
在此问题代码的最低级别,request()
是异步的,因此其调用者getFile()
是异步的,其调用者getInfo()
也是异步的。< / p>
Promise是异步流程结果的抽象,有助于对这些流程完成时要采取的行动进行编码 - 成功或失败。
通常,低级别异步函数应该返回一个承诺,由其调用者执行操作,这反过来会向调用者返回一个promise,依此类推调用堆栈。在每个函数内部,可以使用promise方法(主要是.then()
)对返回的promise做出操作,并且可以使用Promise.all()
进行聚合(例如语法不同)。
在这个问题中,没有证据表明request()
当前返回了一个承诺。您有三种选择:
request()
确实会返回一个承诺。request()
以返回承诺。request()
的适配器函数(&#34; promisifier&#34;),并生成/返回承诺,稍后根据request()
的结果履行或拒绝承诺。 第一个或第二个选项是理想的,但我(Roamer)的安全假设是假设需要一个适配器。幸运的是,我从问题中知道足够能够写一个。 Cheerio
似乎不包含jQuery的promise实现,因此需要一个专用的promise lib。
这是一个适配器函数,使用的语法将与Bluebird lib或本机js promises一起使用:
//Promisifier for the low level function request()
function requestAsync(url) {
return new Promise(function(resolve, reject) {
request(url, function(err, resp, page) {
if (err) {
reject(err);
} else {
if (resp.statusCode !== 200) {
reject(new Error('request error: ' + resp.statusCode));
}
} else {
resolve(page);
}
});
});
}
现在可以编写getFile(...)
和getInfo()
来使用从最低级别的适配器返回的承诺。
//intermediate level function
function getFile(DOMelement) {
var fullUrl = ...;//something derived from DOMelement. Presumably either .val() or .text()
return requestAsync(fullUrl).then(function (page) {
var $ = cheerio.load(page);
srcEp = $(???).attr('src');//Not too sure what the selector should be. `$(this)` definitely won't work.
return srcEp;
});
}
//high level function
function getInfo(path) {
var a = [], b = [], c = [];//presumably
// Now map the $(path) to an array of promises by calling getFile() inside a .map() callback.
// By chaining .then() a/b/c are populated when the async data arrives.
var promises = $(path).map(function(i, e) {
return getFile(e).then(function(srcEp) {
a[i] = ...;
b[i] = e;
c[i] = srcEp;
});
});
//return an aggregated promise to getInfo's caller,
//in case it needs to take any action on settlement.
return Promise.all(promises).then(function() {
//What to do when all promises are fulfilled
var entry = new DB...//(a,b,c)
addToDB(entry);
}, function(error) {
//What to do if any of the promises fails
console.log(error);
//... you may want to do more.
});
}