将异步加载图像中的数据保存到类成员中



我想要一个类来保存必须首先加载的图像的分辨率。它会打印出Width = undefined。问题当然是在回调函数被调用之前调用了打印函数。在我等待一段时间后,它将打印Width = 20。我也不确定将this保存到originalThis并将其传递给回调函数是否有效。代码应该是什么样的,这样它就可以在没有随机等待时间的情况下工作?我想这是你和承诺一起工作的场景吧?

class MarkerSize {
markerWidth;
markerHeight;
constructor(url) {
this.getMeta(url, this.saveData);
}
getMeta(url, callback) {
let img = new Image();
img.src = url;
let originalThis = this;
img.onload = function () {
callback.call(originalThis, this.width, this.height);
};
}
saveData(width, height) {
this.markerWidth = width;
this.markerHeight = height;
}
printSize() {
console.log(`Width = ${this.markerWidth}`);
}
}
let myMarker = new MarkerSize(
'https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png'
);
myMarker.printSize(); //prints Width = undefined
//Here, I use a timeout function to wait for the image. I know this is really bad style.
setTimeout(function () {
myMarker.printSize(); //prints Width = 20
}, 3000);

你说得对,引入Promise将有助于解决这个问题。

printSize方法将是异步的,并将等待loadingPromise

async printSize() {
await this.loadingPromise; // wait for image to be loaded and markerWidth to be set
console.log(`Width = ${this.markerWidth}`);
}

loadingPromise应该在构造函数中创建,并在图像加载时解析。

constructor(url) {
this.loadingPromise = new Promise(resolve => {
this.getMeta(url, (w, h) => {
this.saveData(w, h);
resolve();
});
});
}

完整代码:

class MarkerSize {
markerWidth;
markerHeight;
loadingPromise;
constructor(url) {
this.loadingPromise = new Promise(resolve => {
this.getMeta(url, (w, h) => {
this.saveData(w, h);
resolve();
});
});
}
getMeta(url, callback) {
let img = new Image();
img.src = url;
let originalThis = this;
img.onload = function () {
callback.call(originalThis, this.width, this.height);
};
}
saveData(width, height) {
this.markerWidth = width;
this.markerHeight = height;
}
async printSize() {
await this.loadingPromise; // wait for image to be loaded and markerWidth to be set
console.log(`Width = ${this.markerWidth}`);
}
}
let myMarker = new MarkerSize(
'https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png'
);
myMarker.printSize(); //prints 'Width = 20' (asynchonously)

根据注释中的建议进行小重写,以避免在constructor中使用回调和创建资源:

class MarkerSize {
url;
size;
constructor(url) {
this.url = url;
this.size = null; // not loading image yet, will be loaded during first call to printSize()
}
getSize() {
return new Promise(resolve => {
let img = new Image();
img.src = this.url;
img.onload = function () { // keep in mind that traditional function and arrow function handle 'this' differently
// here this is bound to Image
resolve({ width: this.width, height: this.height });
};
});
}
async printSize() {
if (!this.size) { // load image if not loaded before
this.size = await this.getSize();
}
console.log(`Width = ${this.size.width}`);
}
}
let myMarker = new MarkerSize(
'https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png'
);
myMarker.printSize(); //prints 'Width = 20' (asynchonously)

以下是我的版本。你认为哪里不好,哪里可以改进?

更新版本

class MarkerSize {
#markerWidth;
#markerHeight;
#url;
constructor(url) {
this.url = url;
}
getMeta() {
return this.#getMetaHelper(this.url)
.then(img => {
this.#saveData(img.width, img.height);
return this;
})
.catch(() => {
const defaultWidthHeight = 10;
this.#saveData(defaultWidthHeight, defaultWidthHeight);
return this;
});
}
//load img
#getMetaHelper(url) {
return new Promise((resolve, reject) => {
let img = new Image();
img.src = url;
img.addEventListener('load', () => resolve(img));
img.addEventListener('error', () => reject());
});
}
#saveData(width, height) {
this.markerWidth = width;
this.markerHeight = height;
}
printSize() {
console.log(`Width = ${this.markerWidth}nHeight = ${this.markerHeight}`);
}
}
let myMarker = new MarkerSize(
'https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png'
);
myMarker.getMeta().then(obj => {
obj.printSize();
});

最新更新