循环内的承诺/ promise.all,在节点中使用psql(pg-promise)
Promises inside for loops / promise.all, using psql (pg-promise) in node
你好,我是 Promises 的新手,我一直在思考如何等待 for 循环中的所有 promises 在进入下一个 then() 之前解决。我看过几个 promise.all 示例,但我不清楚如何针对我的以下代码调整它们。它当前在 for 循环之后转到下一个 then() 并在 for 循环完成之前解析。感谢您的帮助!
我正在使用 pg-promise(带有承诺的 psql)。
原码:
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;
}
我是 pg-promise 的作者。
以下是关于在现已删除的答案中在此上下文中无效使用 Promise.all
的一些注意事项。
在使用表示物理资源的基于承诺的接口时,了解所使用的物理上下文非常重要。没有它,您将面临 运行 陷入瓶颈的风险,因为物理资源不会像您的通用承诺解决方案那样扩展。
在 pg-promise
的情况下,您的物理环境由两件事组成:
- 要通过 Node.js IO
传输的查询字符串
- 连接池提供的连接上下文
每个查询请求从连接池中获取和释放一个连接,连接池是非常有限的物理资源。池的默认大小为 10,由底层驱动程序 node-postgres
设置。虽然您可以将它增加到最多 100,但这样做会开始在连接管理上造成过载,因此它不是那么可扩展的。典型的增加是设置为20,大约是平均值。
因此,如果您在查询数组上使用 Promise.all
,您的应用程序几乎会立即耗尽池,并且对您服务的任何下一个请求都将坐在那里等待可用连接。
这样的解决方案根本无法扩展,它在此处被列为查询执行的反模式:Tasks versus root/direct queries.
基本上,它解释的是你必须通过任务执行多个查询:
通过这种方式,您可以通过单个连接传输所有查询,这对于实现服务的可扩展性至关重要。
Learn By Example tutorial for both Tasks and Transactions 中有很多示例。
考虑到您正在尝试获取多个父行,然后是多个子行,您应该看看这个问题:。
我还建议阅读 Performance Boost 文章,以更好地理解执行多个查询的物理限制以及如何解决它们。
例子
function getTeamMembers(aTeam) {
return db.task(t=> {
return t.map('SELECT * FROM team_members WHERE id=', aTeam.id, tm=> {
return t.any('SELECT * FROM users WHERE name=', 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
});
这不是唯一的方法,但它是最短的;)
在以下问题中更好地考虑了这种方法:。
你好,我是 Promises 的新手,我一直在思考如何等待 for 循环中的所有 promises 在进入下一个 then() 之前解决。我看过几个 promise.all 示例,但我不清楚如何针对我的以下代码调整它们。它当前在 for 循环之后转到下一个 then() 并在 for 循环完成之前解析。感谢您的帮助!
我正在使用 pg-promise(带有承诺的 psql)。
原码:
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;
}
我是 pg-promise 的作者。
以下是关于在现已删除的答案中在此上下文中无效使用 Promise.all
的一些注意事项。
在使用表示物理资源的基于承诺的接口时,了解所使用的物理上下文非常重要。没有它,您将面临 运行 陷入瓶颈的风险,因为物理资源不会像您的通用承诺解决方案那样扩展。
在 pg-promise
的情况下,您的物理环境由两件事组成:
- 要通过 Node.js IO 传输的查询字符串
- 连接池提供的连接上下文
每个查询请求从连接池中获取和释放一个连接,连接池是非常有限的物理资源。池的默认大小为 10,由底层驱动程序 node-postgres
设置。虽然您可以将它增加到最多 100,但这样做会开始在连接管理上造成过载,因此它不是那么可扩展的。典型的增加是设置为20,大约是平均值。
因此,如果您在查询数组上使用 Promise.all
,您的应用程序几乎会立即耗尽池,并且对您服务的任何下一个请求都将坐在那里等待可用连接。
这样的解决方案根本无法扩展,它在此处被列为查询执行的反模式:Tasks versus root/direct queries.
基本上,它解释的是你必须通过任务执行多个查询:
通过这种方式,您可以通过单个连接传输所有查询,这对于实现服务的可扩展性至关重要。
Learn By Example tutorial for both Tasks and Transactions 中有很多示例。
考虑到您正在尝试获取多个父行,然后是多个子行,您应该看看这个问题:
我还建议阅读 Performance Boost 文章,以更好地理解执行多个查询的物理限制以及如何解决它们。
例子
function getTeamMembers(aTeam) {
return db.task(t=> {
return t.map('SELECT * FROM team_members WHERE id=', aTeam.id, tm=> {
return t.any('SELECT * FROM users WHERE name=', 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
});
这不是唯一的方法,但它是最短的;)
在以下问题中更好地考虑了这种方法: