我在这里遇到了JS Promises的一些基本问题。这是我的完整代码
'use strict'
const rp = require('request-promise');
const cheerio = require('cheerio');
var fs = require('fs');
var os = require('os');
const options = {
uri: url,
normalizeWhitespace: true,
transform: function (body) {
return cheerio.load(body);
}
};
let results = []
let results2 = []
rp(options)
.then(($) => {
$('.col-xs-4 .grid-item').each(function (i, elem) {
let temp = $(this).find(".prod-image").attr("style")
let productImageUrl = temp.substring(temp.indexOf("background-image:url('") + 22, temp.indexOf("')"))
let detailUrl = $(this).find(".prod-image").attr("href")
let title = $(this).find(".title").text()
let description = $(elem).children().eq(4).attr("content")
results.push({
"productImageUrl": productImageUrl,
"detailUrl": detailUrl,
"title": title,
"description": description
})
});
})
.then(() => {
results.forEach(item => {
const options1 = {
uri: item.detailUrl,
normalizeWhitespace: true,
transform: function (body) {
return cheerio.load(body);
}
};
rp(options1)
.then(($) => {
console.log(5)
let temp = $('#prod-title').text()
let unit = temp.substring(temp.indexOf('Size: ') + 6, temp.indexOf('mL') - 1)
let retail = temp.substring(temp.indexOf('Retail: $') + 9, temp.indexOf(' A'))
let wholesale = temp.substring(temp.indexOf('Wholesale: $') + 12, temp.indexOf(' A') + 21)
results2.push({
"productImageUrl": item.productImageUrl,
"detailUrl": item.detailUrl,
"title": item.title,
"description": item.description,
"unit": unit,
"retail": retail,
"wholesale": wholesale
})
})
.catch((err) => {
console.log(err);
});
})
})
.finally(() => {
console.log("FINALLY " + results2)
let header = "Handle,Title,Body" + os.EOL
fs.writeFile("./file.csv", header, function (err) {
if (err) {
return console.log(err);
}
});
for (let item of results2) {
console.log(2)
let hyphenateTitle = item.title.replace(/\s+/g, '-').toLowerCase();
let line = hyphenateTitle + "," + item.title + "," + item.description + "," + vendor + ',"","",true,Title,Default Title,,,,,SKU,10000,,1,deny,manual,' + item.retail + "," + item.wholesale + "," + 'true,true,"",' + item.productImageUrl + "," + ',1,,false,,,,,,,,,,,,,,,,,kg,' + os.EOL
fs.appendFile("./file.csv", line, function (err) {
if (err) {
return console.log(err);
} else {
// done
}
})
}
})
.catch((err) => {
console.log(err);
});
这个想法是,在第一个then()
中,我将阅读HTML页面并找到一些URL。我会将这些信息推送到results
数组中。
然后,在第二个then
上,其想法是遍历数组中的每个项目并转到第二页,提取更多信息并将其推送到results
Finally
,将全部信息保存在csv中。
这是控制台中的输出:
FINALLY
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
因此,您可以看到最后一个then
在中间一个之前(但在最上面的一个之后)运行。
我在做什么错? 谢谢
编辑1 这是一个评论者建议的新代码:
'use strict'
const rp = require('request-promise');
const cheerio = require('cheerio');
var fs = require('fs');
var os = require('os');
const options = {
uri: url,
normalizeWhitespace: true,
transform: function (body) {
return cheerio.load(body);
}
};
let results = []
let results2 = []
rp(options)
.then(($) => {
console.log("FIRST THEN")
$('.col-xs-4 .grid-item').each(function (i, elem) {
let temp = $(this).find(".prod-image").attr("style")
let productImageUrl = temp.substring(temp.indexOf("background-image:url('") + 22, temp.indexOf("')"))
let detailUrl = $(this).find(".prod-image").attr("href")
let title = $(this).find(".title").text()
let description = $(elem).children().eq(4).attr("content")
results.push({
"productImageUrl": productImageUrl,
"detailUrl": detailUrl,
"title": title,
"description": description
})
});
})
.then(($) => {
console.log("SECOND THEN")
return Promise.all(results.map( item => {
console.log("SECOND THEN INNER")
const options1 = {
uri: item.detailUrl,
normalizeWhitespace: true,
transform: function (body) {
return cheerio.load(body);
}
};
rp(options1)
.then(($) => {
console.log("SECOND THEN INSIDE 'rp(options1)'")
let temp = $('#prod-title').text()
let unit = temp.substring(temp.indexOf('Size: ') + 6, temp.indexOf('mL') - 1)
let retail = temp.substring(temp.indexOf('Retail: $') + 9, temp.indexOf(' A'))
let wholesale = temp.substring(temp.indexOf('Wholesale: $') + 12, temp.indexOf(' A') + 21)
results2.push({
"productImageUrl": item.productImageUrl,
"detailUrl": item.detailUrl,
"title": item.title,
"description": item.description,
"unit": unit,
"retail": retail,
"wholesale": wholesale
})
})
.catch((err) => {
console.log(err);
});
}))
})
.finally(($) => {
console.log("FINALLY " + results2)
let header = "Handle,Title,Body" + os.EOL
fs.writeFile("./file.csv", header, function (err) {
if (err) {
return console.log(err);
}
});
for (let item of results2) {
console.log(2)
let hyphenateTitle = item.title.replace(/\s+/g, '-').toLowerCase();
let line = hyphenateTitle + "," + item.title + "," + item.description + "," + vendor + ',"","",true,Title,Default Title,,,,,SKU,10000,,1,deny,manual,' + item.retail + "," + item.wholesale + "," + 'true,true,"",' + item.productImageUrl + "," + ',1,,false,,,,,,,,,,,,,,,,,kg,' + os.EOL
fs.appendFile("./file.csv", line, function (err) {
if (err) {
return console.log(err);
} else {
// done
}
})
}
})
.catch((err) => {
console.log(err);
});
这是输出:
FIRST THEN
SECOND THEN
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
FINALLY
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
答案 0 :(得分:1)
在第二个then
中,您正在运行异步迭代,没有任何内容告诉包装好的Promise等待循环中的Promises(在forEach
中)。
您应该return
内部的Promise链通知父级Promise等待,并且由于您需要等待迭代中的所有Promise,因此可以使用Promise.all
。
.then( () => {
return Promise.all(results.map( item => {
//...
return rp(options1).then(($) => {
//..
});
}));
})
根据您的需要,您可以在本地catch
(在内部Promise上)或在父Promise上。
请查看Promise documention的详细信息,请参阅common mistakes with Promises部分