【问题标题】:Jquery firing too soon in Node with Promise.all使用 Promise.all 在 Node 中触发 Jquery 太快了
【发布时间】:2022-11-05 06:34:29
【问题描述】:

我在 Electron 和 Node 中有一些异步代码,每个循环在 Jquery 中运行,然后在完成后做出反应:

$(async() => {
  if (await window.electronAPI.delete_items(project.name)) {
    var _count = 0;
    $.each(project.items, function(i, item) {
      var promises = item.subitems.map(async function(subitem) {
        return await window.electronAPI.update_item(subitem);
      });

      Promise.all(promises).then(function() {
        _count++;
        if (_count >= project.items.length) {
          $("#overlay").css("display", "none");
          console.log("Done");
        }
      });
    });
  }
});

但是,所有这些都可以正常工作:

$( "#overlay" ).css( "display", "none" );

立即运行,而:

console.log( "Done" );

更新所有项目后正确运行。

我需要做些什么来防止 JQuery 过早运行?

谢谢 :)

编辑以添加完整代码:

index.js
--------------------------------------

app.whenReady().then( () => {

  ...

    ipcMain.handle( "delete_all_cached_images", delete_all_cached_images );
    ipcMain.handle( "update_cached_image", update_cached_image );

    /* Create the window */
    createWindow();

    app.on( "activate", () => {
        
        /* Fix MacOS multi window bug */
        if( BrowserWindow.getAllWindows().length === 0 ) createWindow();
    } );
} );

async function update_cached_image( e, project_name, image_type, image_size, image_name, image_data ) {

    /* Create a new blank image */
    let image = new Jimp( image_size, image_size, function ( err, image ) {
        
        if( err )
            return false;

        /* Map the pixels correct */
        var _image_data = Array.from( { length: image_size }, () => Array.from( { length: image_size }, () => undefined ) );

        for( var row_sel = 0; row_sel < image_size; row_sel++ )
            for( var col_sel = 0; col_sel < image_size; col_sel++ )
                _image_data[ col_sel ][ row_sel ]  = image_data.data[ row_sel ][ col_sel ];

        /* Loop through each pixel to add to the image */
        _image_data.forEach( ( row, y ) => {

            row.forEach( ( colour, x ) => {

                /* If we have a transparent pixel, don't add it to the image */
                if( ( colour == "" ) || ( colour == null ) )
                    image.setPixelColor( Jimp.rgbaToInt( 0, 0, 0, 0 ), parseInt( x ), parseInt( y ) );
                else 
                    image.setPixelColor( Jimp.cssColorToHex( "#" + colour ), parseInt( x ), parseInt( y ) );
            } );
        } );

        /* Resize to a nice large size */
        image.resize( 512, 512 , Jimp.RESIZE_NEAREST_NEIGHBOR );

        /* Save the image to project directory */
        image.write( path.join(  __dirname, "projects", project_name, "cache", image_type, image_name + ".png" ), ( err ) => {
        
            if( err )
                return false;
        } );
    } );

    return true;
}

preload.js
--------------------------------------
const { contextBridge, ipcRenderer } = require( "electron" );

contextBridge.exposeInMainWorld( "electronAPI", {

    ...

    delete_all_cached_images: ( project_name ) => ipcRenderer.invoke( "delete_all_cached_images", project_name ),
    update_cached_image: ( project_name, image_type, image_size, image_name, image_data ) => ipcRenderer.invoke( "update_cached_image", project_name, image_type, image_size, image_name, image_data )
} );

renderer.js
--------------------------------------
function update_cached_images() {

    $( async () => {

        /* First delete all old cached images */
        if( await window.electronAPI.delete_all_cached_images( project.name ) ) {


            var ti = 0;
            Promise.all( project.textures.map( g_texture => {
                
                return Promise.all( g_texture.textures.map( texture => {

                    return window.electronAPI.update_cached_image( project.name, "textures", 8, ( g_texture.name + "_" + ti++ ), texture );
                } ) );
            } ) ).then( () => {
                
                $("#overlay").css("display", "none");
                console.log("Done");
            } ).catch(err => {

                console.log(err);
                // add something to handle/process errors here
            } );
            
        } else {

            show_error( "Error caching project textures." );
        }
    } );
}

【问题讨论】:

  • return await window.electronAPI.update_item(subitem); 不应该有 await。您想要返回承诺,以便可以将它们放入 Promise.all() 将等待的数组中。
  • 什么跑得太早了?你想让它等什么?在$.each() 中有两个嵌套循环$.each().map()。那么,您是否要等待所有循环完成?或者尝试在每次 $.each() 迭代结束时运行一些东西?
  • @jfriend00 是的,没错,它循环遍历项目组,每个项目组也有很多子项目。因此,一旦两个循环都完成,console.log 函数就会完全按预期触发。但是由于某种原因,JQuery 函数会立即触发。
  • @Barmar 即使使用 await,promise* 也会(从外部角度)立即返回,因为该函数是异步的。 (*:实际上是等效的,但这没关系)我错过了什么吗?
  • @GSephElec 你所描述的对我来说没有意义。您是否通过在语句上设置断点来验证它?您还可以在元素上设置 DOM 断点以查看是否有任何内容别的是先修改它。

标签: jquery node.js async-await promise electron


【解决方案1】:

您可以摆脱计数器,并在调用$( "#overlay" ).css( "display", "none" ); 之前正确等待所有承诺完成,如下所示:

$(async () => {
    if (await window.electronAPI.delete_items(project.name)) {
        Promise.all(project.items.map(item => {
            return Promise.all(item.subitems.map(subitem => {
                return window.electronAPI.update_item(subitem);
            }));
        })).then(() => {
            $("#overlay").css("display", "none");
            console.log("Done");
        }).catch(err => {
            console.log(err);
            // add something to handle/process errors here
        });
    }
});

当然,这假设 window.electronAPI.update_item(subitem); 返回一个仅在完成后才能正确解析的承诺。如果它没有返回一个承诺,或者如果承诺只有在完成后才解决,那么我们也必须修复它。

这是update_cached_image() 的固定版本,它返回一个在完成时解析的承诺:

function update_cached_image(e, project_name, image_type, image_size, image_name, image_data) {
    return new Promise((resolve, reject) => {
        /* Create a new blank image */
        new Jimp(image_size, image_size, function(err, image) {
            if (err) {
                reject(err);
                return;
            }

            /* Map the pixels correct */
            var _image_data = Array.from({ length: image_size }, () => Array.from({ length: image_size }, () => undefined));

            for (var row_sel = 0; row_sel < image_size; row_sel++)
                for (var col_sel = 0; col_sel < image_size; col_sel++)
                    _image_data[col_sel][row_sel] = image_data.data[row_sel][col_sel];

            /* Loop through each pixel to add to the image */
            _image_data.forEach((row, y) => {

                row.forEach((colour, x) => {

                    /* If we have a transparent pixel, don't add it to the image */
                    if ((colour == "") || (colour == null))
                        image.setPixelColor(Jimp.rgbaToInt(0, 0, 0, 0), parseInt(x), parseInt(y));
                    else
                        image.setPixelColor(Jimp.cssColorToHex("#" + colour), parseInt(x), parseInt(y));
                });
            });

            /* Resize to a nice large size */
            image.resize(512, 512, Jimp.RESIZE_NEAREST_NEIGHBOR);

            /* Save the image to project directory */
            image.write(path.join(__dirname, "projects", project_name, "cache", image_type, image_name + ".png"), (err) => {
                if (err) {
                    reject(err);
                    return;
                } else {
                    resolve(image);
                }
            });
        });
    });
}

【讨论】:

  • 这更优雅,谢谢。但我仍然遇到同样的问题,#overlay div 立即隐藏,几秒钟后,console.log 函数触发。
  • @GSephElec - 然后,显然,update_item() 没有正确返回完成后解决的承诺。我不知道电子 - 你能指向那个 API 的参考页面吗?
  • 如果有帮助,这里有一段视频:imgur.com/a/AVa8Zs5
  • 它来自 ipcRenderer 模块:electronjs.org/docs/latest/api/…
  • @GSephElec - 我在该 ipcRenderer 链接中没有看到任何 .update_item() 方法的代码。我需要确切地知道该函数的作用(查看其代码或查看其文档)。看起来你认为它返回一个承诺,当它完成它所做的一切时,它就会解决,但显然情况并非如此。
猜你喜欢
  • 2010-11-28
  • 1970-01-01
  • 2015-08-27
  • 2016-11-08
  • 1970-01-01
  • 1970-01-01
  • 2011-08-06
  • 2016-03-21
  • 1970-01-01
相关资源
最近更新 更多