如何在节点的变量中存储异步函数的结果?例如,我想解析一个网站并获取一些信息,但是我不想不断地请求数据,我只想获取所有页面一次。这是代码:
var cheerio = require('cheerio');
var request = require('request');
function SiteParser() {
const SITE = 'http://www.anywebsite.com';
// variable for caching html
var $ = getBody();
function getBody(SITE){
// request html
request(SITE, function(error, response, html) {
if (!error && response.statusCode == 200) {
return cheerio.load(html);
}
else {
throw new Error('Could not parse web site\nError text: ' + error);
}
})
}
//declaration of class methods using cached html
this.getCategories = function() {};
this.getNews = function() {};
}
当我调用类的方法时,如何确定将收到响应?
还是这是不好的做法?
最佳答案
请注意,var $ = getBody();
不能按您期望的方式工作,因为getBody
不会返回任何内容。内部回调返回了一些东西,但是返回值永远丢失了。此外,该回调是异步调用的,因此您无法通过var $ = getBody();
来获取它,因为getBody
在执行回调之前已完成。
因此,这是解决此问题的快速方法。构造函数采用可选的回调函数作为参数:
function SiteParser(onready) {
const SITE = 'http://www.anywebsite.com';
// variable for caching html
var $;
var that = this; // maintain reference to this instance of SiteParser
function getBody(SITE){
// request html
request(SITE, function(error, response, html) {
if (!error && response.statusCode == 200) {
$ = cheerio.load(html); // store the result
that.ready() // notify client that html has been retrieved
}
else {
throw new Error('Could not parse web site\nError text: ' + error);
}
})
}
//declaration of class methods using cached html
this.getCategories = function() {
if ($ === undefined) throw new Error('Not ready yet');
// extract categories from $ and return them
};
this.getNews = function() {
if ($ === undefined) throw new Error('Not ready yet');
// extract news from $ and return it
};
this.ready = onready || function() {}; // callback for being notified
}
现在您可以编写:
parser = new SiteParser;
parser.ready = function () {
var cats = parser.getCategories();
// ...etc
};
或者:
parser = new SiteParser(function () {
var cats = parser.getCategories();
// ...etc
});
诺言
上面的代码公开了getCategories和getNews方法,即使尚未检索HTML。这不是很好。
使用promise概念,您可以通过仅在所有准备就绪时提供SiteParser实例来对此进行改进。这是一些可以与promise for nodejs一起使用的(未经测试的)代码:
var Promise = require('promise');
// SiteParser now needs to be given the HTML in the constructor:
function SiteParser(html) {
var $ = $cheerio.load(html)
//declaration of class methods using cached html
this.getCategories = function() {
// extract categories from $ and return them
};
this.getNews = function() {
// extract news from $ and return it
};
}
// a separate Promise object takes care of the retrieval:
function promiseSiteParser() {
const SITE = 'http://www.anywebsite.com';
return new Promise(function (resolve, reject) {
// request html
request(SITE, function(error, response, html) {
if (!error && response.statusCode == 200) {
resolve(new SiteParser(html));
}
else {
reject(error);
}
})
});
}
promiseSiteParser().then(function (parser) {
var cats = parser.getCategories();
// ...etc
}, function (error) {
throw new Error('Could not parse web site\nError text: ' + error);
});