我有这个 routine 函数,它在 setInterval 函数上每60000ms运行一次。在此例程函数内部,我遍历已解析的JSON(db)上的所有用户名,并检查是否可以通过作为网络请求的promise(checkUsername)使用。 >
但是,我很清楚这是一种糟糕的方法,因为承诺可能需要60秒钟才能完成,而且我到处都遇到了ETIMEDOUT错误。但我只是不了解承诺和异步性,以至于无法想到解决方案。
有什么更好的方法呢? 异步/等待适合这里吗?
function routine() {
db.users.forEach(userObj => {
userObj.username.forEach(username => {
checkUsername(username).then(hasUser => {
if(!hasUser) {
bot.sendMessage(userObj.chatId, `‼️ Username @${username} is now AVAILABLE ‼️`);
removeName(username, userObj.chatId);
}
}).catch(err => {
console.log(err);
})
})
});
}
setInterval(routine, 120000);
答案 0 :(得分:1)
var interval = 60000;
function routine() {
return Promise.all(db.users.map(userObj => {
return Promise.all(userObj.username.map(username => {
return checkUsername(username).then(hasUser => {
if(!hasUser){
return removeName(username, userObj.chatId).then(function(){
return bot.sendMessage(userObj.chatId, `‼️ Username @${username} is now AVAILABLE ‼️`)
})
}
}))
})).then(function(){
setTimeout(routine, interval);
}, function(error){
console.error(error);
setTimeout(routine, interval);
})
}
routine();
这将每60秒加上解析所有请求所花费的时间运行一次。如果失败,它将在60秒后再次运行。如果用户过多,可能会遇到超时问题。另外,如果“ removeName”失败,则“ bot.sendMessage”将不会运行。
您可以用许多不同的方式来结束承诺。这取决于您需要兑现承诺。
.then(function(){
//do stuff
setTimeout(routine, interval);
}, function(error){
console.error(error);
setTimeout(routine, interval);
})
或
.then(function(){
//do stuff
setTimeout(routine, interval);
}).catch(function(error){
console.error(error);
setTimeout(routine, interval);
})
或
.catch(function(error){
console.error(error);
}).finally(function(){
setTimeout(routine, interval);
})
或
.finally(function(){
setTimeout(routine, interval);
})
答案 1 :(得分:0)
我制作了一个可以运行的代码段,它使用Promise.all
以及async / await ES7来使您的代码更易于处理和理解。仅仅为了一个完整的示例,我也遇到了一个在线发现的真实API端点。
如果您想要该选项,我还添加了一种永久停止超时的方法。
// How often the timeout will run.
// Since the timeout is dependent on when all the requests finish, the timeout will run this many ms after all the requests finish.
var interval = 5000;
// This is what your db seems to resemble.
var db = {
users: [{
username: ['1']
},
{
username: ['2']
},
{
username: ['3']
},
{
username: ['4']
},
]
};
// This will hold the timeout function so you can cancel it at a later time.
var timeoutFn;
// Returns returns a single Promise that resolves when all of the promises it contains have resolved/rejected. It rejects with the first promise that rejects.
function routine() {
console.log("-- Running routine function --");
// Return an array of promises. Please see my comments at the bottom of this whole answer which questions db architecture with username being an array.
// I'm also using map instead of forEach because map automatically returns and array.
let promiseArray = db.users.map(userObj => {
return Promise.all(userObj.username.map(username => {
// This processUsername() function should do all the work related to the username. It helps to keep the routine function as clean as possible since there's already a lot happening in here.
return processUsername(username);
}));
});
// Returns an array of array of promises. This means that every single promise within each array (see above) has to resolve before the `then` runs. If any reject, `catch` will run instead.
return Promise.all(promiseArray).then(() => {
runRoutineAgain();
}).catch((err) => {
console.log('err:', err)
});
}
// This will create a timeout and run routine after interval.
function runRoutineAgain() {
timeoutFn = setTimeout(routine, interval);
}
// This async function returns a promise
async function processUsername(username) {
// Make API call to get data
console.log('Processing username for', username);
// I'm using this free API endpoint online just for the sake of making a complete example. Obviously your API will be returning very different data.
return await fetch(`https://jsonplaceholder.typicode.com/todos/${username}`)
.then(response => response.json())
.then((json) => {
console.log(json);
// This is where you can do your processing.
// if(!hasUser) {
// bot.sendMessage(userObj.chatId, `‼️ Username @${username} is now AVAILABLE ‼️`);
// removeName(username, userObj.chatId);
// }
});
}
function stopTimeout() {
clearTimeout(timeoutFn);
}
routine();
基本上,我正在使用Promise.all
来捕获并等待单个承诺的结果,这非常有用,因为您需要获取大量数据的用户。
随时打开Web控制台以更好地查看输出数据。
我还使用async
/ await
ES7语法,只是为了演示其他(更容易说一些)编写Promises的方式。我了解Promise可能令人生畏,因此以下是一些在尝试学习它们时确实触手可及的链接。
还有没有理由为什么要在原始代码中遍历用户的每个用户名?
db.users.forEach(userObj => {
userObj.username.forEach(username => {…
如果userObj的用户名只有1个,则第二个循环会增加不必要的复杂性。但是,如果您的数据库有一个userObj的多个用户名,那就完全可以了!
答案 2 :(得分:-1)
一种方法是在开始处理间隔功能后立即清除间隔。然后,当您的间隔功能完成时,您可以重新开始间隔。这样,在执行间隔功能时间隔不会滴答作响。看看下面的代码。
let interval = setInterval(routine, 1000)
function routine() {
let sequence = Promise.resolve()
sequence
.then(function () {
clearInterval(interval)
return Promise.resolve()
})
// ... chain rest of your promise here.
.then(function (value) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log('I take long time.');
resolve()
}, 5000)
})
})
.then(function () { // in last promise, start the timer again.
interval = setInterval(routine, 1000)
})
}
在这里,间隔功能应该每1s运行一次,但是promise本身需要5s。因此,该方法要做的第一件事是停止计时器,然后执行其工作,最后重新启用计时器。
超时,
let timeout = setTimeout(routine, 1000)
function routine() {
let sequence = Promise.resolve()
sequence
.then(function () {
return Promise.resolve()
})
// ... chain rest of your promise here.
.then(function (value) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log('I take long time.');
resolve()
}, 5000)
})
})
.then(function () { // in last promise, start the timer again.
timeout = setTimeout(routine, 1000)
})
}