我在一个小的RSS阅读器上,我想在每篇文章中加载第一张图片,将其设置为主图像。
function getNewsImage(content, request){
var $container = $('<div/>').html(content);
var result = {
links:[],
images:[]
};
$container.find('a[href],img').each(function() {
if(this.tagName.toUpperCase() == 'A') {
result.links.push([this.tagName,this.innerHTML,this.href]);
} else {
result.images.push([this.tagName,this.src,this.alt]);
}
});
var image;
if(request=='link'){
image = result.links[0][1];
}else{
if(typeof result.images[0] !== 'undefined'){
var img = document.createElement('img')
img.src = result.images[0][1]
img.onload = function() {
if(this.width > 20){
image = result.images[0][1];
return false;
}else{
image = 'images/noImage.png';
return false;
}
}
}else{
image = 'images/noImage.png';
}
return image;
}
}
不幸的是它没有返回任何东西。问题是函数img.onload = function(){}不会改变变量图像的值......
有人能告诉我我做错了吗?
答案 0 :(得分:1)
有几个问题:
你有竞争条件。您需要在设置onload
之前挂钩src
。否则,当您挂钩onload
时,该事件可能已被触发。只需交换这两个陈述。 (潜伏者:如果您认为没有竞争条件,请参阅下面的“关于#1的更多内容”。)
您正在image
处理程序中设置局部变量onload
。并且您从image
函数返回getNewsImage
。但是在onload
函数返回之后,您的getNewsImage
处理程序将被称为,因此在您等待的情况下,它返回的值将为undefined
图片。您必须修改getNewsImage
以接受它在拥有图片时调用的回调,无法使用return
返回它。
...以及附带问题:如果您最终使用的是链接而不是图片,则可以将image
设置为链接的HTML,但不会返回image
。 return image;
语句在 else
子句中仅处理图像。 (请参阅我对有关一致,明确的代码缩进的问题的评论。)
......我正在努力应对回调..你能否给我一个如何解决这个问题的提示?
目前,您调用getNewsImage
的代码大概如下:
var image = getNewsImage(someContent, someRequest);
if (image.src) {
doSomethingWithTheImage(image);
}
else {
doSomethingWithTheLinkText(image);
}
// ...and so on...
你要改变它,以便你像这样使用它:
getNewsImage(someContent, someRequest, function(image) {
if (image.src) {
doSomethingWithTheImage(image);
}
else {
doSomethingWithTheLinkText(image);
}
// ...and so on...
});
然后getNewsImage
的签名变为如下所示:
function getNewsImage(content, request, callback){
// The new bit -----------------------^^^^^^^^^^
return image;
中你永远不会getNewsImage
。相反,您将其更改为调用传入的回调并为该回调提供图像:
callback(image);
...并确保您始终以异步方式 。
E.g:
function getNewsImage(content, request, callback) {
// Change -----^^^^^^^^^^^
var $container = $('<div/>').html(content);
var result = {
links: [],
images: []
};
var async = true; // <== Change
$container.find('a[href],img').each(function () {
if (this.tagName.toUpperCase() == 'A') {
result.links.push([this.tagName, this.innerHTML, this.href]);
}
else {
result.images.push([this.tagName, this.src, this.alt]);
}
});
if (request == 'link') {
done(result.links[0][1]); // <== Change
}
else {
if (typeof result.images[0] !== 'undefined') {
var img = document.createElement('img')
img.onload = function () {
async = false; // <== Change
if (this.width > 20) {
done(result.images[0][1]); // <== Change
}
else {
done('images/noImage.png'); // <== Change
}
}
img.src = result.images[0][1]; // <== Change (moved under `onload`)
}
else {
done('images/noImage.png'); // <== Change
}
}
// New (note this is *within* getNewsImage)
function done(image) {
if (async) {
setTimeout(function() {
callback(image);
}, 0);
}
else {
callback(image);
}
}
}
合同是这样的:getNewsImage
将与结果异步调用回调。注意,在上面,即使我们同步得到结果,我们仍然安排异步回调;合同是一致的很重要,因为当涉及onload
时回调将是异步的,我们确保总是异步(如果需要,使用setTimeout(..., 0)
)。
更多关于#1:
是的,对于那些想知道,竞争条件确实存在,即使浏览器上的JavaScript是单线程的(除非使用网络工作者)。浏览器实现完全有权查看img
src
get set,查看图像是否在缓存中,并在其内部事件中对该图像上的所有load
处理程序进行回调队列(因为没有,所以没有排队)。在当前运行的JavaScript返回到浏览器之前,不允许触发那些回调,但允许对它们进行排队,这就是我们参加比赛的原因。