Hello I am new to Promises and am stuck on how to wait for all promises within a for loop to resolve before going to the next then(). I have seen a couple of promise.all examples, but it wasn't clear to me how to adapt them for my the following code. It currently goes to the next then() after the for loop and resolves before the for loop is complete. Any help is appreciated!
您好,我是Promises的新手,并坚持如何等待for循环中的所有promise,然后再转到next()。我看过几个promise.all示例,但我不清楚如何根据以下代码调整它们。它目前转到for循环后的next(),并在for循环完成之前解析。任何帮助表示赞赏!
I am using pg-promise (psql with promises).
我正在使用pg-promise(带有promises的psql)。
Original code:
function getTeamMembers(aTeam) {
let promise = new Promise(function(resolve, reject) {
db.getTeamMembers(aTeam.tid) //return sql results rows
.then(function(rows){
for(let i=0; i<rows.length; ++i) { //loop through each result row
getUserByUsername(rows[i].username)
.then(function(cfUser) { //add user from row to aTeam object
aTeam.addMember(cfUser);
})
.catch(function(e) {
reject(e);
});
}
})
.then(function(){
console.log(aTeam); //confirm added properly
resolve(aTeam); //resolve object
})
.catch(function(e) {
console.log('addMemberToTeamByUsername: '+e.stack);
reject(e);
});
});
return promise;
}
1 个解决方案
#1
2
I am the author of pg-promise.
我是pg-promise的作者。
Below is a few considerations about invalid use of Promise.all
in this context within an answer that's now removed.
以下是在此上下文中无效使用Promise.all的一些注意事项,这些注释现在已被删除。
When using a promise-based interface that represents physical resources, it is important to understand the physical context that’s used. Without it you are risking to run into bottlenecks from the simple fact that physical resources do not scale like your generic promise solutions.
使用表示物理资源的基于承诺的界面时,了解所使用的物理上下文非常重要。没有它,你就有可能遇到瓶颈,因为物理资源不像你的通用承诺解决方案那样扩展。
In case of pg-promise
your physical context is made up by two things:
在pg-promise的情况下,您的物理环境由两件事组成:
- Query strings to be piped through Node.js IO
- Connection context provided by the connection pool
查询要通过Node.js IO传输的字符串
连接池提供的连接上下文
Each query request acquires and releases a connection from the connection pool, which is a very limited physical resource. Your default size of the pool is 10, as set by the underlying driver node-postgres
. And although you can increase it to up to 100, doing so will start creating an overload on the connections management, so it is not that scalable. A typical increase is to be set to 20, which is about the average.
每个查询请求从连接池获取和释放连接,连接池是非常有限的物理资源。池的默认大小为10,由底层驱动程序node-postgres设置。虽然您可以将其增加到100,但这样做会开始在连接管理上创建过载,因此它不具有可扩展性。典型的增加量应设为20,约为平均值。
So, if you use Promise.all
on your array of queries, your app will deplete the pool almost instantly, and any next request into your service will just sit there waiting for available connections.
因此,如果您在查询数组中使用Promise.all,您的应用程序几乎会立即耗尽该池,并且您服务中的任何下一个请求都将坐在那里等待可用的连接。
Such solution cannot scale at all, and it is listed as an anti-pattern for query execution here: Tasks versus root/direct queries.
这样的解决方案根本无法扩展,它在此处列为查询执行的反模式:任务与根/直接查询。
Basically, what it explains there is you must execute multiple queries through tasks:
基本上,它解释的是你必须通过任务执行多个查询:
- Method task, if you are not changing data
- Method tx (transaction), if you are changing the data
方法任务,如果您不更改数据
方法tx(事务),如果要更改数据
This way you can pipe all your queries through a single connection, which is essential to allow scalability of your service.
这样,您可以通过单个连接来管理所有查询,这对于实现服务的可伸缩性至关重要。
There's plenty of examples within Learn By Example tutorial for both Tasks and Transactions.
对于任务和事务,在“按示例学习”教程中有大量示例。
And considering that you are trying to get mutiple parent rows, and then multiple child rows, you should look at this question: get JOIN table as array of results with PostgreSQL/NodeJS.
考虑到你试图得到多个父行,然后是多个子行,你应该看看这个问题:用PostgreSQL / NodeJS得到JOIN表作为结果数组。
I would also suggest reading the Performance Boost article, to better understand the physical limitations of executing mutiple queries, and how to solve them.
我还建议阅读Performance Boost文章,以更好地理解执行多个查询的物理限制,以及如何解决它们。
Example
function getTeamMembers(aTeam) {
return db.task(t=> {
return t.map('SELECT * FROM team_members WHERE id=$1', aTeam.id, tm=> {
return t.any('SELECT * FROM users WHERE name=$1', tm.username)
.then(users=> {
tm.users = users;
return tm;
});
}).then(t.batch);
});
}
// usage example:
getTeamMembers({id: 123})
.then(members=> {
// members = array of member objects
})
.catch(error=> {
// error
});
This is not the only way to do it, but it is the shortest ;)
这不是唯一的方法,但它是最短的;)
This approach is given a better consideration in the following question: get JOIN table as array of results with PostgreSQL/NodeJS.
在下面的问题中,这种方法得到了更好的考虑:使用PostgreSQL / NodeJS将JOIN表作为结果数组。
#1
2
I am the author of pg-promise.
我是pg-promise的作者。
Below is a few considerations about invalid use of Promise.all
in this context within an answer that's now removed.
以下是在此上下文中无效使用Promise.all的一些注意事项,这些注释现在已被删除。
When using a promise-based interface that represents physical resources, it is important to understand the physical context that’s used. Without it you are risking to run into bottlenecks from the simple fact that physical resources do not scale like your generic promise solutions.
使用表示物理资源的基于承诺的界面时,了解所使用的物理上下文非常重要。没有它,你就有可能遇到瓶颈,因为物理资源不像你的通用承诺解决方案那样扩展。
In case of pg-promise
your physical context is made up by two things:
在pg-promise的情况下,您的物理环境由两件事组成:
- Query strings to be piped through Node.js IO
- Connection context provided by the connection pool
查询要通过Node.js IO传输的字符串
连接池提供的连接上下文
Each query request acquires and releases a connection from the connection pool, which is a very limited physical resource. Your default size of the pool is 10, as set by the underlying driver node-postgres
. And although you can increase it to up to 100, doing so will start creating an overload on the connections management, so it is not that scalable. A typical increase is to be set to 20, which is about the average.
每个查询请求从连接池获取和释放连接,连接池是非常有限的物理资源。池的默认大小为10,由底层驱动程序node-postgres设置。虽然您可以将其增加到100,但这样做会开始在连接管理上创建过载,因此它不具有可扩展性。典型的增加量应设为20,约为平均值。
So, if you use Promise.all
on your array of queries, your app will deplete the pool almost instantly, and any next request into your service will just sit there waiting for available connections.
因此,如果您在查询数组中使用Promise.all,您的应用程序几乎会立即耗尽该池,并且您服务中的任何下一个请求都将坐在那里等待可用的连接。
Such solution cannot scale at all, and it is listed as an anti-pattern for query execution here: Tasks versus root/direct queries.
这样的解决方案根本无法扩展,它在此处列为查询执行的反模式:任务与根/直接查询。
Basically, what it explains there is you must execute multiple queries through tasks:
基本上,它解释的是你必须通过任务执行多个查询:
- Method task, if you are not changing data
- Method tx (transaction), if you are changing the data
方法任务,如果您不更改数据
方法tx(事务),如果要更改数据
This way you can pipe all your queries through a single connection, which is essential to allow scalability of your service.
这样,您可以通过单个连接来管理所有查询,这对于实现服务的可伸缩性至关重要。
There's plenty of examples within Learn By Example tutorial for both Tasks and Transactions.
对于任务和事务,在“按示例学习”教程中有大量示例。
And considering that you are trying to get mutiple parent rows, and then multiple child rows, you should look at this question: get JOIN table as array of results with PostgreSQL/NodeJS.
考虑到你试图得到多个父行,然后是多个子行,你应该看看这个问题:用PostgreSQL / NodeJS得到JOIN表作为结果数组。
I would also suggest reading the Performance Boost article, to better understand the physical limitations of executing mutiple queries, and how to solve them.
我还建议阅读Performance Boost文章,以更好地理解执行多个查询的物理限制,以及如何解决它们。
Example
function getTeamMembers(aTeam) {
return db.task(t=> {
return t.map('SELECT * FROM team_members WHERE id=$1', aTeam.id, tm=> {
return t.any('SELECT * FROM users WHERE name=$1', tm.username)
.then(users=> {
tm.users = users;
return tm;
});
}).then(t.batch);
});
}
// usage example:
getTeamMembers({id: 123})
.then(members=> {
// members = array of member objects
})
.catch(error=> {
// error
});
This is not the only way to do it, but it is the shortest ;)
这不是唯一的方法,但它是最短的;)
This approach is given a better consideration in the following question: get JOIN table as array of results with PostgreSQL/NodeJS.
在下面的问题中,这种方法得到了更好的考虑:使用PostgreSQL / NodeJS将JOIN表作为结果数组。