如何在 while 循环中串联承诺
How to chain promises in series in a while loop
我必须连续查询多个数据库。基本上是一组必须按顺序完成的承诺。我在一个 while 循环中实现它。数据库是根据日期命名的。我还必须将请求延迟设置为 400 毫秒乘以每个承诺之间传递的循环量 (400*count)。如果它有帮助,我正在使用 cloudant 作为我的数据库,类似于 mongodb/couchdb 但是一个在线实例。
所以我实现了一个查询函数,它将获取开始日期和结束日期,并将检索范围内的所有值。如果日期相同,那么它将查询一个数据库,否则它将为日期范围之间的每一天查询多个数据库。正如您在下面看到的,我主要在 if 语句中的 else 块上遇到了麻烦。
db.predQuery = (start, end, locationCode) => {
return new Promise((resolve, reject) => {
let data = [];
const startDate = moment(start, "MM/DD/YYYY");
const endDate = moment(end, "MM/DD/YYYY");
if (startDate.diff(endDate) === 0) {
// THIS IF BLOCK WORKS GOOD
let dbName = `prediction_${String(locationCode)}_`;
dbName += startDate.format("YYYY-MM-DD");
const db = this.cloudant.use(dbName);
return db.find({selector: {_id: {'$gt': 0}}}).then((body) => {
data = body.docs;
return resolve(data);
});
} else {
// THIS BLOCK IS WHERE THE PROBLEM IS
// This is to start off the promise chain in the while loop
let chainProm = Promise.resolve(1);
let iterator = moment(startDate);
let count = 0;
// While loop for the series of promises
while (iterator.isBefore(endDate) || iterator.isSame(endDate)) {
//dbName Format: prediction_0_2019-05-28
let dbName = `prediction_${String(locationCode)}_`;
dbName += iterator.format("YYYY-MM-DD");
const db = this.cloudant.use(dbName);
count += 1;
// Set off chain of promises
chainProm = chainProm.then(() => {
setTimeout(()=>{
db.find({selector: {_id: {'$gt': 0}}}).then((body) => {
// Keep adding on to the old array
data = data.concat(body.docs);
});
},count*400);
});
// Move to the next day
iterator.add(1,'days');
}
// Once all done resolve with the array of all the documents
return resolve (data);
}
})
};
基本上,输出应该是日期范围内所有文档的数组。现在单个日期有效,但是当我执行一系列日期时,请求没有通过,或者我达到了限制,或者它说承诺从未得到解决。我认为这可能不是解决此问题的最佳方法,并且对任何解决方案都持开放态度。非常感谢任何帮助。
您的 chainProm
没有连接到 predQuery
的外部调用 - resolve
正在立即被调用。
您可能会发现使用 async
/await
更容易,delay-inside-loop 逻辑会更容易理解:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
db.predQuery = async (start, end, locationCode) => {
const startDate = moment(start, "MM/DD/YYYY");
const endDate = moment(end, "MM/DD/YYYY");
if (startDate.diff(endDate) === 0) {
// THIS IF BLOCK WORKS GOOD
let dbName = `prediction_${String(locationCode)}_`;
dbName += startDate.format("YYYY-MM-DD");
const db = this.cloudant.use(dbName);
const body = await db.find({selector: {_id: {'$gt': 0}}});
return body.docs;
}
const iterator = moment(startDate);
const data = [];
const testShouldIterate = () => iterator.isBefore(endDate) || iterator.isSame(endDate);
let shouldIterate = testShouldIterate();
while (shouldIterate) {
//dbName Format: prediction_0_2019-05-28
let dbName = `prediction_${String(locationCode)}_`;
dbName += iterator.format("YYYY-MM-DD");
const db = this.cloudant.use(dbName);
const body = await db.find({selector: {_id: {'$gt': 0}}});
data.push(body.docs);
// Move to the next day
iterator.add(1,'days');
shouldIterate = testShouldIterate();
// Don't delay on the final iteration:
if (!shouldIterate) {
break;
}
await delay(400);
}
// Once all done resolve with the array of all the documents
return data;
};
使用此实现,任何错误都将发送给 predQuery
的调用者(错误将导致 Promise it returns 被拒绝),因此在调用 predQuery
时,确保在后面加上 catch
。
我还没有完全理解你的代码。但是,如果查询彼此独立,那么 Promise.all()
似乎可以解决您的问题:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
如果您实际上需要以同步顺序对 运行 的承诺,请尝试简单地等待循环中的承诺解决。
我必须连续查询多个数据库。基本上是一组必须按顺序完成的承诺。我在一个 while 循环中实现它。数据库是根据日期命名的。我还必须将请求延迟设置为 400 毫秒乘以每个承诺之间传递的循环量 (400*count)。如果它有帮助,我正在使用 cloudant 作为我的数据库,类似于 mongodb/couchdb 但是一个在线实例。
所以我实现了一个查询函数,它将获取开始日期和结束日期,并将检索范围内的所有值。如果日期相同,那么它将查询一个数据库,否则它将为日期范围之间的每一天查询多个数据库。正如您在下面看到的,我主要在 if 语句中的 else 块上遇到了麻烦。
db.predQuery = (start, end, locationCode) => {
return new Promise((resolve, reject) => {
let data = [];
const startDate = moment(start, "MM/DD/YYYY");
const endDate = moment(end, "MM/DD/YYYY");
if (startDate.diff(endDate) === 0) {
// THIS IF BLOCK WORKS GOOD
let dbName = `prediction_${String(locationCode)}_`;
dbName += startDate.format("YYYY-MM-DD");
const db = this.cloudant.use(dbName);
return db.find({selector: {_id: {'$gt': 0}}}).then((body) => {
data = body.docs;
return resolve(data);
});
} else {
// THIS BLOCK IS WHERE THE PROBLEM IS
// This is to start off the promise chain in the while loop
let chainProm = Promise.resolve(1);
let iterator = moment(startDate);
let count = 0;
// While loop for the series of promises
while (iterator.isBefore(endDate) || iterator.isSame(endDate)) {
//dbName Format: prediction_0_2019-05-28
let dbName = `prediction_${String(locationCode)}_`;
dbName += iterator.format("YYYY-MM-DD");
const db = this.cloudant.use(dbName);
count += 1;
// Set off chain of promises
chainProm = chainProm.then(() => {
setTimeout(()=>{
db.find({selector: {_id: {'$gt': 0}}}).then((body) => {
// Keep adding on to the old array
data = data.concat(body.docs);
});
},count*400);
});
// Move to the next day
iterator.add(1,'days');
}
// Once all done resolve with the array of all the documents
return resolve (data);
}
})
};
基本上,输出应该是日期范围内所有文档的数组。现在单个日期有效,但是当我执行一系列日期时,请求没有通过,或者我达到了限制,或者它说承诺从未得到解决。我认为这可能不是解决此问题的最佳方法,并且对任何解决方案都持开放态度。非常感谢任何帮助。
您的 chainProm
没有连接到 predQuery
的外部调用 - resolve
正在立即被调用。
您可能会发现使用 async
/await
更容易,delay-inside-loop 逻辑会更容易理解:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
db.predQuery = async (start, end, locationCode) => {
const startDate = moment(start, "MM/DD/YYYY");
const endDate = moment(end, "MM/DD/YYYY");
if (startDate.diff(endDate) === 0) {
// THIS IF BLOCK WORKS GOOD
let dbName = `prediction_${String(locationCode)}_`;
dbName += startDate.format("YYYY-MM-DD");
const db = this.cloudant.use(dbName);
const body = await db.find({selector: {_id: {'$gt': 0}}});
return body.docs;
}
const iterator = moment(startDate);
const data = [];
const testShouldIterate = () => iterator.isBefore(endDate) || iterator.isSame(endDate);
let shouldIterate = testShouldIterate();
while (shouldIterate) {
//dbName Format: prediction_0_2019-05-28
let dbName = `prediction_${String(locationCode)}_`;
dbName += iterator.format("YYYY-MM-DD");
const db = this.cloudant.use(dbName);
const body = await db.find({selector: {_id: {'$gt': 0}}});
data.push(body.docs);
// Move to the next day
iterator.add(1,'days');
shouldIterate = testShouldIterate();
// Don't delay on the final iteration:
if (!shouldIterate) {
break;
}
await delay(400);
}
// Once all done resolve with the array of all the documents
return data;
};
使用此实现,任何错误都将发送给 predQuery
的调用者(错误将导致 Promise it returns 被拒绝),因此在调用 predQuery
时,确保在后面加上 catch
。
我还没有完全理解你的代码。但是,如果查询彼此独立,那么 Promise.all()
似乎可以解决您的问题:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
如果您实际上需要以同步顺序对 运行 的承诺,请尝试简单地等待循环中的承诺解决。