提问者:小点点

循环中的setTimeout会停止脚本工作


我的脚本正在从API接收数据并自动存储在MongoDB中。 我需要创建一个至少2秒的延迟,然后再接收一个又一个数据。 问题是我的剧本第二次就停止工作了。 假设我的脚本每小时工作一次,我在14.00启用脚本-它工作,在15.00停止。 我开始研究这个问题,并指出循环中的setTimeout()存在问题。

这是我在For Loop#js中使用settimeout()时发现的一篇文章

这一行是一个节点调度包,它基本上每15分钟调用一次脚本(如果有人想知道是这样的话)

const j = schedule.scheduleJob('*/15 * * * *', callIt)

我的目标:我如何改变我的代码仍然有2秒的延迟和工作循环。 是否有任何替代setTimeout()的方法,可能我只需要将setTimeout()放在代码中的另一个位置,或者甚至有某种类型的包我可以添加额外的。

有问题的代码区域:

    var symbols = ["ZRXBTC", "ETHBTC", "ETCBTC", "KAVABTC", "AEBTC"]
];
let cnt = 0;

const callIt = () => {

    fetch(`https://api.binance.com/api/v3/klines?symbol=${symbols[cnt]}&interval=30m&limit=1`)
        .then(res => res.json())
        .then(data => {
            const btcusdtdata = data.map(d => {
                return {
                    Open: parseFloat(d[1]),
                    High: parseFloat(d[2]),
                    Low: parseFloat(d[3]),
                    Close: parseFloat(d[4]),
                    Volume: parseFloat(d[5]),
                    Timespan: 30,
                }
            });
            console.log(btcusdtdata);
            saveToDatebase(btcusdtdata);
            cnt++;
            if (cnt < symbols.length) setTimeout(callIt, 2000)
        })
        .catch((err) => {
            console.log(err);
        })
};

全码

var requestPromise = require('request-promise');
const { MongoClient } = require('mongodb');
const schedule = require('node-schedule');
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
const fetch = require("node-fetch");

var symbols = ["ZRXBTC", "ETHBTC", "ETCBTC", "KAVABTC", "AEBTC"]
    ];
    let cnt = 0;

    const callIt = () => {

        fetch(`https://api.binance.com/api/v3/klines?symbol=${symbols[cnt]}&interval=30m&limit=1`)
            .then(res => res.json())
            .then(data => {
                const btcusdtdata = data.map(d => {
                    return {
                        Open: parseFloat(d[1]),
                        High: parseFloat(d[2]),
                        Low: parseFloat(d[3]),
                        Close: parseFloat(d[4]),
                        Volume: parseFloat(d[5]),
                        Timespan: 30,
                    }
                });
                console.log(btcusdtdata);
                saveToDatebase(btcusdtdata);
                cnt++;
                if (cnt < symbols.length) setTimeout(callIt, 2000)
            })
            .catch((err) => {
                console.log(err);
            })
    };

const j = schedule.scheduleJob('*/15 * * * *', callIt)

const saveToDatebase = function(BTCdata) {

    const url = 'mongodb+srv://username:password@cluster0-1kunr.mongodb.net/<dbname>?retryWrites=true&w=majority';

    var today = new Date();
    var date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate();
    var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
    var dateTime = date + ' ' + time;

    MongoClient.connect(url, { useNewUrlParser: true, useUnifiedTopology: true }, (err, db) => {
        if (err) throw err;
        const dbo = db.db('CryptoCurrencies');
        const myobj = { Name: symbols[cnt - 1], Array: BTCdata, Date: dateTime };
        dbo.collection(`${symbols[cnt - 1]}`).insertOne(myobj, (error, res) => {
            if (error) throw error;
            console.log('1 document inserted');
            db.close();
        });
    });

};

编辑1:为了更正确,我将更精确地指定任务。 我的node-schedule包假设每15分钟调用一次脚本,而我希望每2秒调用一次数组中的属性。 我需要调用属性从数组每2秒,否则我将收到IP禁令从Binance API调用API到much/fast。

编辑2好的。 setInterval()不是我的解决方案。 因为我需要每15分钟调用一次脚本,所以它应该经过array,当它从array调用所有属性时,它应该停止。 在setInterval()中,调用数组中的所有属性后,再次启动它,不幸的是,这不是我需要的。

编辑3:我测试了下面答案中的几个选项,所有这些选项都让我发现了相同的问题,即脚本无法第二次启动,或者脚本立即开始工作,或者即使数组属性结束后仍然重复。 还是谢谢你的回答,但我的问题还是一个。

目前我正在尝试使用async/await方法。 但我收到一个错误,Await只在异步函数中有效

这是一个来自@YoavMatchulsky的完整代码解决方案。 脚本开始工作,但我没有收到任何数据什么的。 就像工作一样--但什么都没发生。 没有差错,什么都没有。

var requestPromise = require('request-promise');
const { MongoClient } = require('mongodb');
const schedule = require('node-schedule');
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
const fetch = require("node-fetch");

const symbols = ["ZRXBTC", "ETHBTC", "ETCBTC", "KAVABTC", "AEBTC"];

const sleep = async(timeout) => {
    return new Promise(resolve => {
        setTimeout(resolve, timeout);
    });
}

const callIt = async(symbol) => {
    return fetch(`https://api.binance.com/api/v3/klines?symbol=${symbol}&interval=30m&limit=1`)
        .then(res => res.json())
        .then(data => async() => {
            const btcusdtdata = data.map(d => {
                return {
                    Open: parseFloat(d[1]),
                    High: parseFloat(d[2]),
                    Low: parseFloat(d[3]),
                    Close: parseFloat(d[4]),
                    Volume: parseFloat(d[5]),
                    Timespan: 30,
                }
            });
            console.log(btcusdtdata);
            await saveToDatebase(btcusdtdata);
        })
        .catch((err) => {
            console.log(err);
        })
};

const saveToDatebase = async function(BTCdata) {
    return new Promise((resolve, reject) => {

        const url = 'mongodb+srv://username:password@cluster0-1kunr.mongodb.net/<dbname>?retryWrites=true&w=majority';

        var today = new Date();
        var date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate();
        var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
        var dateTime = date + ' ' + time;

        MongoClient.connect(url, { useNewUrlParser: true, useUnifiedTopology: true }, (err, db) => {
            if (err) {
                return reject(err);
            }
            const dbo = db.db('CryptoCurrencies');
            const myobj = { Name: symbols[cnt - 1], Array: BTCdata, Date: dateTime };
            dbo.collection(`${symbols[cnt - 1]}`).insertOne(myobj, (error, res) => {
                if (error) {
                    return reject(error);
                }
                console.log('1 document inserted');
                db.close();
                resolve();
            });
        });
    });
};

const run = async() => {
    let cnt = 0;
    while (cnt < symbols.length) {
        await callIt(symbols[cnt]);
        await sleep(2000);
        cnt++;
    }
}

const j = schedule.scheduleJob('*/2 * * * *', run);

共3个答案

匿名用户

请尝试使用setInterval。

setInterval(callIt,2000年);

但将其置于callIt函数之外。

https://developer.mozilla.org/en-us/docs/web/api/windoworworkerglobalscope/setinterval

匿名用户

下面是一个async/await解决方案:

const callIt = async () => {
  try {
    let res = await fetch(
      `https://api.binance.com/api/v3/klines?symbol=${symbols[cnt]}&interval=30m&limit=1`
    );
    let data = await res.json();
    const btcusdtdata = data.map(d => {
      return {
        Open: parseFloat(d[1]),
        High: parseFloat(d[2]),
        Low: parseFloat(d[3]),
        Close: parseFloat(d[4]),
        Volume: parseFloat(d[5]),
        Timespan: 30
      };
    });
    console.log(btcusdtdata);
    saveToDatebase(btcusdtdata);
    cnt++;
    if (cnt < symbols.length) {
      await sleep(2000);
      callIt();
    }
  } catch (err) {
    console.log(err);
  }
};

function sleep(ms) {
  return new Promise(res => setTimeout(res, ms));
}

匿名用户

有什么替代setTimeout()的方法吗

假设您只想每2秒执行一些代码,而不是使用循环,使用具有2秒延迟的settInterval

setInterval(() => {
   // code here will run every 2 seconds
}, 2000);

我需要每15分钟调用一次脚本,它应该经过数组,当它从数组调用所有属性时,它应该停止

下面是一个代码示例,它每15秒调用run函数,并以2秒的延迟访问每个数组元素。

第一个setInterval函数每15秒调用一次run函数,第二个setInterval函数在run函数中以2秒的延迟访问每个数组元素。 访问完所有数组元素后,取消此间隔。

null

const symbols = ["ZRXBTC", "ETHBTC", "ETCBTC", "KAVABTC", "AEBTC"];

console.log('wait 15 seconds before run function is called');
setInterval(() => {
  run();
}, 15000);

function run() {
  console.log('running code after 15 seconds interval');
  let index = 0;
  const id = setInterval(() => {
    console.log(symbols[index]);
    index++;
    
    if (index >= symbols.length) {
      console.log('all array indexes accessed');
      clearInterval(id);
    }
  }, 2000);
}