setInterval

时间:2019-02-14 19:07:51

标签: javascript node.js

我有这个 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);

3 个答案:

答案 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可能令人生畏,因此以下是一些在尝试学习它们时确实触手可及的链接。

  1. https://javascript.info/promise-basics-这涵盖了承诺
  2. https://javascript.info/async-await-这涉及异步/等待

还有没有理由为什么要在原始代码中遍历用户的每个用户名?

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)
        })
}