我在使用程序代码时使用事件代码时遇到了很多麻烦。我有一个函数合并图像,它被称为每秒20-40次。有时候它有效,有时却没有。有时在图像加载之前调用drawImage,有时则不会。这是代码(它是一个类的方法)
merge(image, pos) {
if (!pos && !image.pos)
pos = [0, 0];
else if (!pos)
pos = image.pos;
canvas.element[3].width = this.width;
canvas.element[4].width = this.width;
canvas.element[3].height = this.height;
canvas.element[4].height = this.height;
canvas.ctx[3].putImageData(this.toImageData(), ...this.pos);
var image1 = new Image(this.width, this.height);
image1.onload = function() {/*help pls*/};
image1.src = canvas.element[3].toDataURL();
canvas.ctx[4].drawImage(image1, 0, 0);
canvas.ctx[3].clearRect(0,0,this.width,this.height);
canvas.ctx[3].putImageData(image.toImageData(), ...pos);
image1 = new Image(this.width, this.height);
image1.onload = function() {/*help pls*/};
image1.src = canvas.element[3].toDataURL();
canvas.ctx[4].drawImage(image1, 0, 0);
this.data.set(new Uint32Array(canvas.ctx[4].getImageData(0,0,this.width,this.height).data.buffer));
return this;
}
我知道我必须在onload
事件中写一些东西。我觉得我已经尝试了一切。我希望函数“看起来”对调用者来说是程序性的。将调用此函数,它会执行此操作并返回结果。我不想输入回调并将所有简单的过程代码转换为事件代码。
我尝试使用while循环“等待”onload
事件触发,但这不起作用(我讨厌循环!)。所有这一切都是崩溃!我尝试将随后的所有内容放入onload
事件中,但在更改后我无法立即返回this
。
我想我在某处可以看到你可以使用递归循环。我依稀记得arguments.callee
。但我不知道如何实现这一点。
答案 0 :(得分:1)
" Javascript是顺序"
在javascript中编写事件驱动代码时要知道的一件重要事情是javascript中的代码执行是严格顺序的。一次一个功能,一次一个指令。你不能打断javascript的执行。
当你写
之类的东西时function doSomething(){
var img = new Image();
img.onload = function(){ // do stuff }; // This can not run until
// your current call is over.
img.src = "foo.img";
// lots more code..
...
...
...
// end of the function
}
//code
doSomething();
// more code.
console.log("Have a nice day"); // last line
// at this point there is no more code
// so javascript returns
// then it checks if there is anything on the call stack.
// It is only now that onload can run.
当你致电doSomething
时,该功能会阻止所有其他javascript。您可以调用函数,但在退出doSomething
之前不会执行异步事件。
这意味着onload
事件必须等到doSomething
函数返回。更确切地说,调用是空的(不再返回),因此如果您从foo()
调用bar()
开始,请调用baf()
。直到foo()
返回并且其下面的所有代码都已完成,任何其他代码都可以运行。
图像加载是异步的,它加载在一个单独的线程上。加载完成后,它不会调用函数onload
,而是将调用放在调用堆栈上。只有一个调用堆栈,并且通过本机异步调用(如图像加载,超时,间隔等)对其进行调用的顺序取决于异步完成的时间。
您的代码中发生的事情是您创建的图像将被您指定原始图像变量的新图像替换。因为没有对原始图像的引用,并且因为在您退出之前不会发生onload,所以javascript假定您不再需要第一个图像并将其转储。
有几种方法可以解决问题。一是确保维持对每个新图像的引用。您可以使用image.addEventListener("load", function(){ //your image is "this'});
执行此操作。这将保留对原始图像的引用,因为事件侦听器与图像分开存储,与image.onload = function(){}
不同,function doSomething(){
var img;
// when the following function is call the argument
// image is closed over. This means that it keeps an unique
// reference to image every time this function is call.
// this means that the image is not replaced below
function addEvent(image){
image.onload = function(){}
}
img = new Image()
img.src = "blah.img"
addEvent(img); // use function closure to close over the image
// thereby maintaining a separate reference to the img
img = new Image(); // replace the previous image with new
img.src = "blah1.img"; // because the addEvent function has closed over
// the previous img and is holding a separate reference
// the image will not be dumped
addEvent(img); // clos over this one making a second reference via closure
}
doSomething();
// not until completely exited that the onload functions can be removed from the
// call stack and executed.
与您分配新图像时图像一起丢失那个变量。
另一种方法是使用闭包来保存对图像的引用。
var imageArray = [];
function doSomething(){
img = new Image()
img.src = "blah.img"
img.onload = function(){};
imageArray.push(img); // copy the image reference to the array.
img = new Image(); // create a new image but the previous one
img.src = "blah.img" // is still referenced and thus wont be lost.
img.onload = function(){};
imageArray.push(img); // copy the image reference to the array.
}
最简单的方法是自己创建个人参考。
chunk := make([]byte, 1024)
..read some data into the chunk but only 10 bytes are filled from the file...
这样做可能有助于让你的头脑更清晰,但这三种方法都做同样的事情。它们为图像创建单独的引用,为每个图像存储图像引用的副本。允许图像存在,直到当前执行返回并且可以触发onload事件。
使用数组当然会用图像填充内存,除非您从数组中删除图像引用。但这是另一个故事。
希望能够解释代码出了什么问题,并为您提供修改代码所需的内容,并继续编写有趣的部分,而不是令人沮丧的部分。