我不确定如何问这个。
我想知道我只有网址的三张图片的大小(不用说,这只是一个例子)。
有些东西告诉我Deferred
是这样做的方式......我在其他情况下使用它但我不知道如何将各种deferred
对象链接在一起,每个对应一个for
循环的迭代,并为所有这些循环提供一个done
函数(它是否有意义?我不知道我是否让自己清楚了。)
这基本上就是我正在尝试使用此代码,但自然这里延迟只会调用done
一次,而不是每个图像。我希望只有在拥有所有图像的尺寸后才能解析Deferred
。
更新
我现在明白了,感谢jgreenberg和Bergi,我需要为每个图片提供一个Deferred
对象,以及一个Promises
数组。新代码:
var imagesURLArray = [
'http://placekitten.com/200/300',
'http://placekitten.com/100/100',
'http://placekitten.com/400/200'];
var promises = [];
function getImageDimensions(url){
var deferred = new $.Deferred();
var img = $('<img src="'+ imagesURLArray[i] +'"/>').load(function(){
deferred.resolve( {width: this.width, height: this.height} );
});
return deferred.promise();
}
for (var i = 0; i < imagesURLArray.length; i++) {
promises.push(getImageDimensions(imagesURLArray[i]));
}
$.when.apply($, promises).then(function(dimensions){
console.log('dimensions: ' + dimensions);
});
但是我仍然无法弄清楚如何从then()
中的所有Deferred对象中检索数据。 dimensions
参数仅返回第一个Deferred对象中的数据。
答案 0 :(得分:2)
您需要使用http://api.jquery.com/jquery.when/
我尝试修改您的代码,但我还没有运行它。我相信你会从这里弄明白。
function getImageDimensions(url){
var deferred = new $.Deferred();
var img = $('<img src="'+ imagesURLArray[i] +'"/>').load(function(){
var dimensions = [this.width, this.height];
deferred.resolve(dimensions);
});
return deferred.promise()
}
var promises = []
for (var i = 0; i < imagesURLArray.length; i++) {
promises.push(getImageDimensions(imagesURLArray[i]));
}
$.when.apply($, promises).then(function(dimensions){
console.log('dimensions: ' + dimensions);
});
答案 1 :(得分:2)
这是一段代码,它扩展了内置的jQuery承诺,适用于图像的.load()
事件。这允许您进行一些非常简单的编码,以等待您的图像组加载。这是jQuery的附加代码,用于支持图像加载的承诺:
(function() {
// hook up a dummy animation that completes when the image finishes loading
// If you call this before doing an animation, then animations will
// wait for the image to load before starting
// This does nothing for elements in the collection that are not images
// It can be called multiple times with no additional side effects
var waitingKey = "_waitingForLoad";
var doneEvents = "load._waitForLoad error._waitForLoad abort._waitForLoad";
jQuery.fn.waitForLoad = function() {
return this.each(function() {
var self = $(this);
if (this.tagName && this.tagName.toUpperCase() === "IMG" &&
!this.complete && !self.data(waitingKey)) {
self.queue(function(next) {
// nothing to do here as this is just a sentinel that
// triggers the start of the fx queue
// it will get called immediately and will put the queue "inprogress"
}).on(doneEvents, function() {
// remove flag that we're waiting,
// remove event handlers
// and finish the pseudo animation
self.removeData(waitingKey).off(doneEvents).dequeue();
}).data(waitingKey, true);
}
});
};
// replace existing promise function with one that
// starts a pseudo-animation for any image that is in the process of loading
// so the .promise() method will work for loading images
var oldPromise = jQuery.fn.promise;
jQuery.fn.promise = function() {
this.waitForLoad();
return oldPromise.apply(this, arguments);
};
})();
这可以通过覆盖当前的.promise()
方法来实现,当为集合中尚未完成加载的每个图像调用.promise()
方法时,它会在jQuery“fx”中启动虚拟动画队列和虚拟动画在图像的“加载”事件触发时完成。因为jQuery已经支持动画的承诺,所以在为尚未加载的每个图像启动虚拟动画之后,它可以只调用原始的.promise()
方法,并且jQuery完成创建承诺的所有工作并跟踪何时当动画全部完成时,动画完成并解决承诺。我真的很惊讶jQuery不会自己这样做,因为它只是少量的额外代码,并且利用了他们已经在做的很多事情。
以下是此扩展程序的测试jsFiddle:http://jsfiddle.net/jfriend00/bAD56/
关于jQuery中内置.promise()
方法的一个非常好的事情是,如果你在一个对象集合上调用它,它将返回给你一个主要的承诺,只有当所有的个人承诺都被解决时已经解决了,所以你不必做所有那些家务管理 - 它会为你做那件肮脏的工作。
关于覆盖.promise()
是否是一个好的方法,这是一个意见问题。对我来说,向现有的.promise()
方法添加一些额外的功能似乎很不错,这样除了动画之外,它还可以让您管理图像load
事件。但是,如果那个设计选择不是你的一杯茶而你宁愿保留现有的.promise()
方法,那么图像加载承诺行为可以放在新方法.loadPromise()
上。要以这种方式使用它,而不是通过分配oldPromise = ...
开始的代码块,您将替换它:
jQuery.fn.loadPromise = function() {
return this.waitForLoad().promise();
};
并且,要检索包含图片load
事件的承诺事件,您只需拨打obj.loadPromise()
而不是obj.promise()
。本文的其余部分和示例假设您正在使用.promise()
覆盖。如果您切换到.loadPromise()
,则必须在剩余的文字/演示中替换它。
此扩展中的概念也可用于DOM中的图像,因为您可以执行此类操作,因此只要加载DOM中的一组图像就可以执行某些操作(无需等待所有图像加载):
$(document).ready(function() {
$(".thumbnail").promise().done(function() {
// all .thumbnail images are now loaded
});
});
或者,与原始问题无关,但也可以通过此扩展程序执行一些有用的操作,您可以等待加载每个图像,然后在加载每个图像时立即启动动画:
$(document).ready(function() {
$(".thumbnail").waitForLoad().fadeIn(2000);
});
每个图像在加载完成时开始淡入(或者如果已加载则立即开始)。
而且,以下是使用上述扩展程序查看代码的方式:
var imagesURLArray = [
'http://placekitten.com/200/300',
'http://placekitten.com/100/100',
'http://placekitten.com/400/200'];
// build a jQuery collection that has all your images in it
var images = $();
$.each(imagesURLArray, function(index, value) {
images = images.add($("<img>").attr("src", value));
});
images.promise().done(function() {
// all images are done loading now
images.each(function() {
console.log(this.height + ", " + this.width);
});
});
工作jsFiddle演示:http://jsfiddle.net/jfriend00/9Pd32/
或者,在现代浏览器中(支持数组上的.reduce()
),您可以使用:
imagesURLArray.reduce(function(collection, item) {
return collection.add($("<img>").attr("src", item));
}, $()).promise().done(function() {
// all images are done loading now
images.each(function() {
console.log(this.height + ", " + this.width);
});
});;