虽然代码起初可能看起来有点沉重(可能无论如何),但它有点简单。我想要做的就是从JSON文件中读取一些数据(格式如下:
{"news":[{}]}
)
,并将一些信息推送到我从另一个来自newsapi.org服务器的JSON文件中获取的数组中。我的问题可能是由吊装造成的,但我不知道如何解决它..!在控制台中,它会吐出Uncaught TypeError: Cannot read property 'title' of undefined
。
以下内容是我已失效代码的摘录。
(我知道我的代码看起来很乱:P)
function appendNews(id) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://newsapi.org/v2/top-headlines?sources=" + id +
"&apiKey=8fdd732e76664e06b177a20fb295129c", true);
xhr.send();
xhr.addEventListener("readystatechange", processRequest, false);
xhr.onreadystatechange = processRequest();
function processRequest() {
if (xhr.readyState == 4 && xhr.status == 200) {
var response = JSON.parse(xhr.responseText);
var container = document.getElementById("newsContainer");
for (var i = 0; i < response.articles.length; i++) {
fs.readFile('./data/events/news.json', 'utf8', function(err, data) {
if (err) {
console.log(err)
} else {
var file = JSON.parse(data)
file.news.push({
"title": response.articles[i].title,
"description": response.articles[i].description,
"url": response.articles[i].url,
"urlImg": response.articles[i].urlToImage,
"time": response.articles[i].publishedAt,
"sourceAndAuthor": response.articles[i].author + " (" +
response.articles[i].source.name + ")"
})
var toStringify = JSON.stringify(file);
fs.writeFile('./data/events/news.json', toStringify, 'utf8', function(err) {
if (err) {
console.log(err);
} else {
}
});
}
});
}
}
编辑:在我的代码中,我使用相同的变量,然后它可以工作:
var holder = document.createElement("button");
holder.setAttribute("id", "newsHolder");
holder.setAttribute("onclick", "openArticle(" + "'" +
response.articles[i].title + "'" + ")")
var title = document.createElement("div");
title.setAttribute("id", "newsTitle");
title.innerHTML = response.articles[i].title;
var image = document.createElement("img");
image.setAttribute("src",response.articles[i].urlToImage)
image.setAttribute("id", "articleImage");
holder.appendChild(title);
holder.appendChild(image);
container.appendChild(holder);
fadeIn("newsContainer",1,0.01,1);
答案 0 :(得分:1)
这是您的控制流程中的一个主要问题:
for (var i = 0; i < response.articles.length; i++) {
fs.readFile('./data/events/news.json', 'utf8', function(err, data) {
...
var file = JSON.parse(data)
...
var toStringify = JSON.stringify(file);
fs.writeFile('./data/events/news.json', toStringify, 'utf8', function(err) {
});
});
}
这样的写作表明完全缺乏对异步编程的理解,因为真正最终发生的是竞争条件,其中最后一次成功fs.writeFile()
只会将response.articles
中的一个条目附加到您的JSON,即使您要修复TypeError
。
您希望代码能够像这样运行:
i=1 2 3 4 5 ... L
r r r r r
p p p p p
w w w w w
r, p, w
分别代表fs.readFile()
,file.news.push()
和fs.writeFile()
,但真正发生的是:
i=1 2 3 4 5 ... L
r r r r r ...
p p p p p ...
w w w w w ...
您的推送和写入将以无保证的顺序发生,它们不一定是顺序的。
其中L
为reseponse.articles.length
,并且您在response.articles[i]
时尝试访问i === response.articles.length
,因此当然不存在article
。< / p>
第一个改变是使用
for (let i = 0; i < response.articles.length; i++) {
但是这不会解决上面解释的竞争条件,它只会导致i
具有词法范围,所以至少你不会访问越界。
为了避免不必要的阅读和写作,您只需fs.readFile()
一次,循环浏览articles
并将其推送到回调中的file.news
,然后fs.writeFile()
在for
循环结束后。这看起来像这样:
// add your event listener BEFORE sending
xhr.addEventListener('load', processRequest, false);
xhr.send();
function processRequest() {
// no need to check readyState and status if you use the `load` event
fs.readFile(..., (err, data) => {
// handle error
if (err) {
console.log(err);
return;
}
const { articles = [] } = JSON.parse(xhr.responseText);
const file = JSON.parse(data);
for (const article of articles) {
const {
title,
description,
url,
urlToImage: urlImg,
publishedAt: time,
author,
source: { name }
} = article;
file.news.push({
title,
description,
url,
urlImg,
time,
sourceAndAuthor: `${author} (${name})`
});
}
const json = JSON.stringify(file);
fs.writeFile(..., (err) => {
if (err) {
console.log(err);
return;
}
});
});
}