0

我在一个小的 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 (){} 不会改变变量 image 的值......

有人可以告诉我我做错了什么吗?

4

1 回答 1

1

几个问题:

  1. 你有一个竞争条件。设置onload 需要先钩住src。否则,当您连接时onload,该事件可能已经被触发。只需交换两个语句。(潜伏者:如果您认为没有竞争条件,请参阅下面的“更多关于 #1”。)

  2. 您正在处理程序中设置一个局部变量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);

...并且您确保始终异步执行此操作。

例如:

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获取集,查看图像是否在缓存中,并load在其内部事件队列中对该图像上的所有处理程序的回调进行排队(因为没有,所以没有排队)。在当前运行的 JavaScript 返回给浏览器之前,不允许触发这些回调,但允许将它们排队,这就是我们进行比赛的原因。

于 2013-07-14T16:18:22.973 回答