简体   繁体   English

检测 ForEach() 循环内的最后一个实例 Promise — JavaScript

[英]Detect The Last Instance Of A ForEach() Loop Inside A Promise — JavaScript

I have some image files that are previewed as part of a file uploader.我有一些图像文件作为文件上传器的一部分进行预览。 A well as validations happening in the backend (PHP) I am setting up frontend validations too.以及在后端 (PHP) 中发生的验证我也在设置前端验证。

When the images are attached to the uploader's file <input> element, a preview thumbnail is generated using URL.createObjectURL()当图像附加到上传者的文件<input>元素时,使用URL.createObjectURL()生成预览缩略图

These previews are looped over and a decode() method inside the loop detects if they fail certain criteria.这些预览会循环播放,循环内的decode()方法会检测它们是否不符合某些条件。 If they fail then an error message is outputted / attached to the specific image file (preview thumbnail) that fails.如果失败,则会输出一条错误消息/附加到失败的特定图像文件(预览缩略图)。

If this error message is detected, a generic error message appears at the top of the page telling the user to check the errors shown on the image previews thumbnails.如果检测到此错误消息,则会在页面顶部显示一条通用错误消息,告诉用户检查图像预览缩略图上显示的错误。

The Issue问题

The generic error message at the top of the page is outputted, but because this all happens inside a loop, if 5 files are attached, and only one fails, the generic error message flashes because it is being detected on each instance of the loop.输出页面顶部的一般错误消息,但由于这一切都发生在循环内,如果附加了 5 个文件,并且只有一个失败,则一般错误消息会闪烁,因为在循环的每个实例上都会检测到它。

The Question问题

How do I detect the last instance of a loop and only output the generic error message when the last loop instance happens?如何检测循环的最后一个实例,并且在最后一个循环实例发生时仅检测 output 一般错误消息?

Note: I have included a simplified example of the code below, but would be happy to show the full code (but I am thinking this might be overkill in relation to the issue).注意:我在下面包含了一个简化的代码示例,但很乐意展示完整的代码(但我认为这对于这个问题来说可能有点矫枉过正)。

submitData = new DataTransfer();

// initial 'change' event listener on the files input element which is stored in a variable 'attachFiles'
attachFiles.addEventListener("change", (e) => {

    // stuff here handles the data transfer method and detects any change in the number of files attached etc

    // then run the 'showFiles function on each file attached
    [...submitData.files].forEach(showFiles);

});

function showFiles(file) {
    let previewImage = new Image();

    // Set relevant <img> attributes
    previewImage.className = "upload-preview-image";
    previewImage.src = URL.createObjectURL(file);

    // get the original width and height values of the thumbnail using the decode() method
    previewImage.decode().then((response) => {

        let w = previewImage.naturalWidth;
        let h = previewImage.naturalHeight;

        // error message that is appended to each image when it fails
        let resError = `<p class="upload-preview-error">Image must be bigger than 2MP</p>`

        if(w * h < 2000000) {
            // append the above errorMessage to the specific image preview in question
            previewImage.insertAdjacentHTML('beforebegin', resError);
        }

        // store HTML class from the above template string in a variable
        let imgError = document.querySelectorAll('.upload-preview-error');

        // set the variable that changes to true when an error message is detected
        let imgErrorMessage = false;

        /* SOMEHOW RUN THIS NEXT CODE ON THE LAST INSTANCE OF THE LOOP, 
        SO THAT THE MAIN ERROR MESSAGE AT THE TOP OF THE PAGE IS ONLY OUTPUTTED ONCE 
        */

        if(imgError.length) {
            if (imgErrorMessage === false) {

                // Append this generic message into a <div> with the class '.js-upload-error'
                document.querySelector('.js-upload-error').innerHTML = `<p class="warning">YOU HAVE ERRORS. CHECK YOUR IMAGES BELOW</p>`

                imgErrorMessage = true;

            }
        }
    }).catch((encodingError) => {
        // Do something with the error.
    });

} // end of showfiles(file)

You look to have a dummy error handling block here:您看起来这里有一个虚拟错误处理块:

}).catch((encodingError) => {
    // Do something with the error.
});

When there are problems, consider throwing an error inside showFiles and then handling it in the caller by using Promise.allSettled to wait for all calls to settle (to either fulfill or reject).当出现问题时,考虑在showFiles中抛出一个错误,然后在调用者中使用Promise.allSettled处理它以等待所有调用完成(完成或拒绝)。

From a UI perspective, you almost certainly also want to actually do something to tell the user when there's some other error - in other words, drop the empty // Do something with the error.从 UI 的角度来看,您几乎肯定还想在出现其他错误时实际做一些事情来告诉用户 - 换句话说,删除empty // Do something with the error. block entirely and let the caller deal with it.完全阻塞并让调用者处理它。 (Or, if you actually do do something with the error in that block, you can re-throw it so that the caller's .allSettled can see that there was a problem.) (或者,如果您确实对该块中的错误做了一些处理,您可以重新抛出它,以便调用者的.allSettled可以看到存在问题。)

function showFiles(file) {
    const previewImage = new Image();
    previewImage.className = "upload-preview-image";
    previewImage.src = URL.createObjectURL(file);
    return previewImage.decode().then((response) => {
        const w = previewImage.naturalWidth;
        const h = previewImage.naturalHeight;
        const resError = `<p class="upload-preview-error">Image must be bigger than 2MP</p>`
        if (w * h < 2000000) {
            previewImage.insertAdjacentHTML('beforebegin', resError);
            throw new Error('Image too small');
        }
    });
}

and replace并更换

[...submitData.files].forEach(showFiles);

with

Promise.allSettled([...submitData.files].map(showFiles))
    .then((results) => {
        if (results.some(result => result.status === 'rejected')) {
            document.querySelector('.js-upload-error').innerHTML = `<p class="warning">YOU HAVE ERRORS. CHECK YOUR IMAGES BELOW</p>`;
            // you can put more error handling here
            // or you can put it in the .catch block if you re-throw
        }
    });

If there's an error other than the image being too small, you might add such an error message in the .catch , such as:如果除了图像太小之外还有其他错误,您可以在.catch中添加这样的错误消息,例如:

return previewImage.decode().then((response) => {
    // ...
})
    .catch((error) => {
        if (error.message !== 'Image too small') {
            previewImage.insertAdjacentHTML('beforebegin', '<p class="upload-preview-error">Decoding error</p>');
        }
        throw error;
    });

or use the .then(success, fail) syntax.或者使用.then(success, fail)语法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM