如何与 forEach 同时执行承诺?

Posted

技术标签:

【中文标题】如何与 forEach 同时执行承诺?【英文标题】:How to execute promises simultaneously with forEach? 【发布时间】:2021-05-14 09:23:06 【问题描述】:

我有一个代码可以在我的系统中更新或创建新用户。它与 forEach 函数一起使用,效果很好。 但是,我想利用 Node.js 并同时执行多个更新/创建。 我试图创建一系列承诺,但我认为我构建的承诺是错误的。 这是可以正常工作的代码:

import usersjson from './jsons/users.json';
if (usersjson.users.length) 
    for (const user of usersjson.users) 
        let getuser = getUser(url, user.Id, 'Id');
            getuser.then((data) => //Make a call to get user by id
                if (data.users.length != 0)  //If user exists
                    let update = updateUser(url, user, data.users[0].id);//update user function
                    update.then((updateresponse) => 
                        console.log(updateresponse);
                        ).catch((err) => 
                            console.log(err);
                        )
                 else  //if user doesn't exist
                    let update = newUser(url, user);//createUser(settings.fieldMap, user)
                    update.then((response) => 
                        console.log(response);
                    ).catch((err) => 
                        console.log(err);
                    );
                
            ) 
    ;
;

现在,我想将“更新”变成 Promise,这样我就可以使用 Promise.all() 同时发送多个。这是我尝试过的:

import usersjson from './jsons/users.json';
let promises = [];
if (usersjson.users.length) 
    for (const user of usersjson.users) 
        if (promises.length <= 20) 
            let getuser = getUser(url, user.Id, 'Id');
            getuser.then((data) => //Make a call to get user by id
                if (data.users.length != 0)  //If user exists
                    let update = new promise ((resolve, reject) => 
                        updateUser(url, user, data.users[0].id).then((response) => 
                            resolve(response);
                        ).catch((err) => 
                            console.log(err)
                        );
                    );
                    promises.push(update);
                 else  //if user doesn't exist
                    let update = new promise ((resolve, reject) => 
                        newUser(url, user).then((response) => 
                            resolve(response);
                        ).catch((err) => 
                            console.log(err);
                        )
                    );
                    psomises.push(update);
                
            );
         else 
            await promises.all(update)
            .then ((result) => 
                console.log(result);
                promises = [];
            ).catch(err)
                console.log(err);
            
        ;
    ;
;

但我收到一条错误消息:

(node:22016) UnhandledPromiseRejectionWarning: ReferenceError: promise 没有定义 在文件:///C:/xampp/htdocs/NodeAPI/server.js:70:21 在 processTicksAndRejections (internal/process/task_queues.js:97:5) (node:22016) UnhandledPromiseRejectionWarning:未处理的承诺拒绝。这 错误源于在异步函数内部抛出 没有 catch 块,或拒绝未处理的承诺 使用 .catch()。在未处理的承诺上终止节点进程 拒绝,请使用 CLI 标志 --unhandled-rejections=strict(请参阅 https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode)。 (拒绝 ID:1)(节点:22016)[DEP0018] 弃用警告:未处理 不推荐使用承诺拒绝。在未来,承诺拒绝 未处理的将终止 Node.js 进程 非零退出代码。

我的问题是,如何让数组中的 20 个用户同时运行 forEach,而不是一个一个地运行?

【问题讨论】:

你为什么要把awaitthen混在一起? 另外,您不再需要使用.forEachfor( of ) 是很久以前添加到 javascript 中的,它适用于任何可迭代对象,包括所有具有 .forEach 函数属性的类型。 因为我显然不知道自己在做什么? :) 我什至不知道为什么我的原始代码需要使用 Promise。我只是被要求这样做。 这段代码比它需要的要复杂得多。我会先以更简单的方式重写它。我不太了解您试图用这段代码完成什么,以及您调用的某些函数做了什么才能建议进行更简单的重写。 另外,你不会“执行承诺”。您执行函数,某些函数可能会返回一个 Promise,它只是一个帮助您监视异步操作的对象。承诺不会“执行”。区别不仅仅是语义,因为它对于理解异步操作和承诺实际发生的事情很重要。 【参考方案1】:

    首先,将处理每个单独的user 值的代码流 移到它自己单独的async 函数中。

    由于 async 函数的实际工作方式(因为每个 async function 标记每个异步状态机的边界),在 for 语句中执行并发异步工作实际上并不可行。

    for 循环中调用该函数。

    总是更喜欢=== 而不是==

    使用 TypeScript。在没有 TypeScript 的情况下使用 async JavaScript 是一个痛苦的世界,因为您需要跟踪哪些函数返回 Promise&lt;T&gt; 与那些不返回的函数,以及正确解包 Promise&lt;T&gt; 对象。

    始终尽可能拥有自己的 try/catch 语句来包装 async 函数的整个主体,否则您将不得不处理不同 JS 引擎和 JS 代码如何处理抛出的异常和对象的不一致。

import usersjson from './jsons/users.json';

async function updateUsers() 
    
    const promises = [];

    const users = usersjson;
    for( const user of users ) 
        const promise = updateUser( user ); // <-- Note this is *NOT* awaited here.
        promises.push( promise );
    

    await Promise.all( promises ); // <-- This is where we await everything.


async function updateUser( user ) 
    
    try 
        const data = await getUser( url, user.Id, 'Id' );
        if( data.users.length === 0 ) 
            let createResult = await newUser( url, user );
            console.log( "Created new user %o", createResult  );
        
        else 
            const updateResult = await updateUser( url, user, data.users[0].id );
            console.log( "Update user %o: Result: %o", user.Id, updateResult );
        
    
    catch( err ) 
        console.error( "Failed to update (or create) user with UserId %o: %o", user.userId, err );
    



【讨论】:

那么如果我想分块做呢?例如,一次 20-50 个用户?在那种情况下,我怎样才能在 for 循环之外等待? 好的,我设法通过异步所有这些所在的函数来正确地做到这一点。非常感谢!【参考方案2】:

实现这一点的最简单方法是在 foarch 方法中,将所有的 Promise 引入一个数组中,然后调用它们,然后使用 Promise.all 等到一切都完成。

let promiseArray = []
array.forEach(x => 
      // Do whatever you need
     promiseArray.push(updateUser(...))
     // Push only the promise without the 'then'
     // You are going to handle the response or 
     // the errors later  

)
const promiseResponses = await Promise.all(promiseArry)
// promiseResponses is an array of responses
// like promiseResponse[0] 

【讨论】:

谢谢!但是,当我尝试使用此方法时,“Promise.all”之前的“await”出现错误“SyntaxError: Unexpected reserved word”。我不能在“forEach”循环中使用“await”,这会阻止我设置一个计数器(我想一次推送大约 50 个承诺,所以在每次“推送”之后我检查数组的长度和如果是 20,我使用“Promise.all”函数。

以上是关于如何与 forEach 同时执行承诺?的主要内容,如果未能解决你的问题,请参考以下文章

Angular forEach 等到所有承诺都完成?

等待内部带有承诺的 forEach 完成

在承诺完成之前退出 forEach

QTextEdit foreach

我怎样才能修复这个承诺,这样我就不会发回未定义的

js中for循环和forEach执行次数的坑