You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firefox中img.onload未触发、Promise始终Pending问题求助

问题:Firefox中Promise.all始终处于pending状态,无法完成Canvas图片叠加

我正在实现从本地存储加载一张或多张图片,叠加到另一张图片后绘制到Canvas的功能。我将每张图片的加载过程封装为Promise,仅在图片加载完成后resolve;待所有Promise都resolve后,再发送生成的图像数据。

Chrome浏览器中可以成功生成叠加图像,控制台日志显示Promise.all的回调已执行;但Firefox浏览器中无法生成叠加图像,代码从未进入Promise.all的回调,Promise始终处于pending状态。

核心实现代码

var Promises = [];
for (const selectedOverlayImage of selectedOverlayImages.values()) {
    Promises.push(new Promise((resolve, reject) => {
        const overlayImg = new Image();
        overlayImg.onerror = () => { console.log(`Image ${selectedImage.value} loading error`); reject(); };
        overlayImg.onload = () => {
            canvasContext.drawImage(overlayImg, 0, 0, canvas.width, canvas.height);
            resolve();
        };
        overlayImg.src = `overlayImages/${selectedOverlayImage.value}.png`;
        console.log(selectedOverlayImage.value);
    }));
}

console.log("1");
var ret = Promise.all(Promises).then(() => {
    console.log("IN");
    let imageData = canvas.toDataURL('image/png');
    fetch("http://localhost:8000/picture-editing.php", { //Call php to create picture in database
        method: "POST",
        headers: {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"},
                body: `imageData=${imageData}`
    });
    alert('Images successfully loaded!'); //This creates a gain of time before the refresh, allowing the new picture to be present more often after refresh
    window.location.reload();
}).catch((error) => {console.log(error);});
console.log("2");
console.log(ret);
setTimeout(() => {
console.log('the queue is now empty');
console.log(ret);
});

错误复现步骤

  • 在index.php中写入以下代码:
<h1 class="pageTitle">Create Picture</h1>
<form method="get">
    <input name="modeOfPicture" type="submit" value="webcam"/>
        <input name="modeOfPicture" type="submit" value="upload"/>
</form>
<div class="wrapperPictureEditing">
<div id="getPicture" class="block1PictureEditing"></div>
<canvas id="takePictureCanvas" style="display:none;" width="320" height="240"></canvas>
<form id="takePictureButton" class="block2PictureEditing">
    <p>Choose Overlay Image:</p>
    <label><input type="checkbox" name="overlay" value="wine"> Wine</label><br>
    <button type="submit">Take photo</button><br>
</form>

<script>
function displaySelectedPicture(input) {
  var selectedPictureDisplay = document.getElementById('selectedPictureDisplay');

  if (input.files && input.files[0]) {
    var reader = new FileReader();

    reader.onload = function (e) {
            selectedPictureDisplay.src = e.target.result;
    };

    reader.readAsDataURL(input.files[0]);
  } else { selectedPictureDisplay.removeAttribute('src') }
}

function takePicture(camera) {
    const takePictureButton = document.getElementById('takePictureButton');

    takePictureButton.addEventListener("submit", () => {
    let canvas = document.getElementById('takePictureCanvas');
        let canvasContext = canvas.getContext('2d');
        selectedOverlayImages = document.querySelectorAll('input[name="overlay"]:checked');
        console.log(selectedOverlayImages);

        if (camera) {
            const stream = document.querySelector('video');
        canvasContext.drawImage(stream, 0, 0, canvas.width, canvas.height);
      } else {
            const selectedImage = document.getElementById('selectedPictureDisplay');
        canvasContext.drawImage(selectedImage, 0, 0, canvas.width, canvas.height);
      }

        var Promises = [];
        for (const selectedOverlayImage of selectedOverlayImages.values()) {
            Promises.push(new Promise((resolve, reject) => {
                const overlayImg = new Image();
                overlayImg.onerror = () => { console.log(`Image ${selectedImage.value} loading error`); reject(); };
                overlayImg.onload = () => {
                    canvasContext.drawImage(overlayImg, 0, 0, canvas.width, canvas.height);
                    resolve();
                };
                overlayImg.src = `image.png`;
                console.log(selectedOverlayImage.value);
            }));
        }

        console.log("1");
        var ret = Promise.all(Promises).then(() => {
            console.log("IN");
            let imageData = canvas.toDataURL('image/png');
            alert('Images successfully loaded!'); //This creates a gain of time before the refresh, allowing the new picture to be present more often after refresh
            window.location.reload();
        }).catch((error) => {console.log(error);});
        console.log("2");
        console.log(ret);
        setTimeout(() => {
        console.log('the queue is now empty');
        console.log(ret);
        });
    });
}

function setupVideoStream(stream) {
  const video = document.querySelector('video');
  video.srcObject = stream;
  video.play();
}

function errorMsg(msg, error) {
  const errorElement = document.getElementById('error');
  errorElement.innerHTML += `${msg}`;
  if (typeof error !== 'undefined') {
    console.error(error);
  }
}

function streamError(error) {
  if (error.name === 'NotAllowedError') {
    errorMsg('Permissions have not been granted to use your camera' +
      ', you need to allow the page access to your camera in ' +
      'order to take pictures. Instead you can upload an image.');
    } else if (error.name === "NotFoundError") {
        errorMsg('No camera has been found on this device. ' +
            'Instead you can upload an image.');
  } else {
    errorMsg(`getUserMedia error: ${error.name}`, error);
  }
}

async function init() {
  var getPictureHTML = document.getElementById('getPicture');
    var modeOfPicture = (new URLSearchParams(document.location.search)).get('modeOfPicture');

    if (modeOfPicture === null || modeOfPicture === "webcam") {
      try {
        const stream = await navigator.mediaDevices.getUserMedia({video: true});
        getPictureHTML.insertAdjacentHTML('afterbegin',
            "<video width='320' height='240' autoplay></video><br>");
            setupVideoStream(stream);
            takePicture(true);
      } catch (error) {
            getPictureHTML.insertAdjacentHTML('afterbegin',
                "<p id='error' class='error'></p>" +
                "<label><input type='file' name='selectedPicture' accept='image/*' " +
                "onchange='displaySelectedPicture(this);'><br>Upload image<br></label>" +
                "<br><img id='selectedPictureDisplay' width='320' height='240'/>");
            streamError(error);
        takePicture(false);
      }
    } else {
        getPictureHTML.insertAdjacentHTML('afterbegin',
            "<label><input type='file' name='selectedPicture' accept='image/*' " +
            "onchange='displaySelectedPicture(this);'><br>Upload image<br></label>" +
            "<br><img id='selectedPictureDisplay' width='320' height='240'/>");
        takePicture(false);
    }
}

init();
</script>
  • 在同一目录下放置名为image.png的图片
  • 在该目录下执行命令启动服务器:php -S localhost:8000
  • 分别在Chrome和Firefox中访问localhost:8000,勾选Wine叠加图后点击拍照,成功时控制台应输出"IN"

内容的提问来源于stack exchange,提问作者artainmo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 17:19:01