【问题标题】:NodeJS - infinite call on promise functionsNodeJS - 对承诺函数的无限调用
【发布时间】:2018-05-18 22:46:15
【问题描述】:

我正在寻找一种方法来无限调用带有承诺的函数。 我尝试了两种情况,一种有效,另一种无效。 不起作用的代码的目的是:从 API 获取数据,然后将其存储到数据库中。

我正在学习承诺,有人可以解释一下为什么一个有效而另一个无效吗? 在我的代码下方

工作代码 该函数只被调用一次,我希望它被无限调用

const request = require('request') //node to facilitate http request
var nano    = require('nano')('http://admin:12345@localhost:5984'); //connect to couchdb using id and password
var db_name  = nano.db.use('bitfinex'); //couchdb database name
var ltc_url = 'https://api.bitfinex.com/v1/pubticker/ltcusd' //entry point
var nonce = new Date().toISOString()  //gives a unique id
                      .replace(/T/, ' ')    // replace T with a space
                      .replace(/\..+/, '')     // delete the dot and everything after

let cleanTheRoom = function() {
      return new Promise(function(resolve, reject) {
        resolve('Cleaned the Room, ');
      });
    };

  let removedTheGarbage = function(msg) {
    return new Promise(function(resolve, reject) {
      resolve(msg + 'removed the garbage, ');
    });
  };

  let getIcecream = function(msg) {
    return new Promise(function(resolve, reject) {
      resolve(msg +'got icecream.');
    });
  };

setInterval(function(){
  cleanTheRoom()
    .then(removedTheGarbage)
    .then(getIcecream)
    .then(function(msg) {
    console.log(msg );
  });
}, 2000);

失败代码

const request = require('request') //node to facilitate http request
    var nano    = require('nano')('http://admin:12345@localhost:5984'); //connect to couchdb using id and password
    var db_name  = nano.db.use('bitfinex'); //couchdb database name
    var ltc_url = 'https://api.bitfinex.com/v1/pubticker/ltcusd' //entry point
    var nonce = new Date().toISOString()  //gives a unique id
                          .replace(/T/, ' ')    // replace T with a space
                          .replace(/\..+/, '')     // delete the dot and everything after

// get current litecoin price from Bitfinex

function getLtcPrice(){
  return new Promise(function(resolve, reject){
    request.get(ltc_url,
        function (error, response, body) {
        var rep = JSON.parse(body);
        var ltc_price = rep.ask;
          resolve (ltc_price)
        if (error){
          reject(ltc_price)
          }
      });
    })
  }

//save current litecoin price to the database

function saveLtcPrice (ltc_price){
      return new Promise(function(resolve, reject){
        resolve(
        db_name.insert({ _id: nonce, currency:"Litecoin", price: ltc_price},
           function(err, body) {
             if (!err)
             console.log(" ltc price : "+ ltc_price +", uploaded to the database ");
          })
          )
      });
    }

setInterval(function(){
  getLtcPrice()
    .then(function(ltcPrice){
      saveLtcPrice(ltcPrice);
    });
}, 2000);

【问题讨论】:

  • 您收到了哪些错误消息。你能提供更多信息吗?
  • 如果再次触发 setInterval 的处理程序时之前的调用尚未完成怎么办?它应该等待之前的请求完成吗?
  • @ZombieChowder :我实际上没有收到任何错误消息,只是它只运行一次

标签: javascript node.js api promise setinterval


【解决方案1】:

在插入完成后重写你的 saveLtcPrice 来解决:

// get current litecoin price from Bitfinex
function getLtcPrice(){
   return new Promise(function(resolve, reject){
       request.get(ltc_url, function (error, response, body) {
           if (error) reject(error)

           var rep = JSON.parse(body);
           var ltc_price = rep.ask;
           resolve (ltc_price)
       });
   })
}

//save current litecoin price to the database
function saveLtcPrice (ltc_price){
    return new Promise(function(resolve, reject){
        db_name.insert({
            _id: nonce,
            currency:"Litecoin",
            price: ltc_price
        }, function(error, body) {
            if(error) reject(error)

            console.log(" ltc price : "+ ltc_price +", uploaded to the database ");
            resolve(body)
      })
  });
}

Promise.resolve().then(function resolver() {
    return getLtcPrice().then(function(ltcPrice){
        return saveLtcPrice(ltcPrice);
    }).then(resolver)// both functions ended, call them again
}).catch((error) => {
    console.log("Error: " + error);
});

【讨论】:

  • 我相信不使用 setTimeout 或 setImmediate 的递归函数将导致 stack overflow。如果它包含在我现在很好奇的承诺中,我可能是错的。
  • @JohnRodney:这不会导致堆栈溢出。 .then(resolver) 安排 resolver 稍后运行,并且不再需要当前 resolver 的堆栈帧。另请参阅requestAnimationFrame 的典型用法。
  • @Ryan 这条评论被放在了这个答案的旧版本上,在那里我做了一个递归函数,在解决承诺后调用自己,这确实导致了堆栈溢出
  • @Valera:我认为不会有
  • 干得好! :) 非常感谢你们的帮助。我仍然需要实践这些承诺以充分了解它应该如何正确设置
【解决方案2】:

我在您的下降代码中看到 2 个错误。在 getLtcPrice 函数中,你应该在解决之前检查错误,所以。

function getLtcPrice(){
  return new Promise(function(resolve, reject){
    request.get(ltc_url, function (error, response, body) {
      if (error) { // check for errors immediatly
        reject(ltc_price)
        return
      }
        var rep = JSON.parse(body);
        var ltc_price = rep.ask;
        resolve(ltc_price)
      });
    })
  }

在 saveLtcPrice 函数中,您始终解决将调用传递给异步函数的问题。这毫无意义。你应该像在 getLtcPrice 函数中那样做,所以:

function saveLtcPrice (ltc_price) {
      return new Promise(function(resolve, reject) {
        db_name.insert({ _id: nonce, currency:"Litecoin", price: 
          ltc_price},function(err, document) { //changed variable name "body" to "document". it's just a convention, this is not the body of an http request, this is an object inside a database
             if (err) {
               reject(err)
               return
             }

             console.log(" ltc price : "+ ltc_price +", uploaded to the database ");
             resolve(document)
          })
          )
      });
    }

最后,您应该在 setInterval 函数中捕获错误

【讨论】:

  • 你仍然有 getLtcPrice 错误:你需要一个 else 以便只调用一个分支:如果有错误,可能没有要解析的主体。
  • @Duncan 不正确。如果你在 Promise 中拒绝,代码执行会立即停止。
  • 这不是任何文档所说的,也不是内置的 Promise 实现所做的。在您的浏览器控制台中尝试:new Promise(function(resolve,reject) { reject('rejected'); console.log('foo'); }) 它将输出 foo 以及拒绝承诺。
  • reject 不会停止,但一旦被拒绝;调用 resolve 不会做任何事情。抛出一个错误也只会返回一个被拒绝的承诺,所以没问题(也不会多次拒绝)。请注意,当您使用 Promise polyfil 时,任何事情都可能发生,因此最好在调用 resolve 或 reject reject(err);return; 后返回
  • @Duncan 毫无疑问,是的 :-) 我个人赞成if (err) reject(err) else resolve(res)
【解决方案3】:

您的 saveLtcPrice 函数对 resolve 的调用位置非常奇怪

function saveLtcPrice (ltc_price){
  return new Promise(function(resolve, reject){
    resolve( /* <--- HERE */
    db_name.insert({ _id: nonce, currency:"Litecoin", price: ltc_price},
       function(err, body) {
         if (!err)
         console.log(" ltc price : "+ ltc_price +", uploaded to the database ");
      })
      )
  });
}

也许你应该在最终回调中数据库操作完成后调用resolve。

function saveLtcPrice (ltc_price){
  return new Promise(function(resolve, reject){
    db_name.insert({ _id: nonce, currency:"Litecoin", price: ltc_price},
       function(err, body) {
         if (!err) {
           resolve(body) /* <-- Move to here */
           console.log(" ltc price : "+ ltc_price +", uploaded to the database ");
         } else {
           reject(err);
         }
       })
  });
}

您尝试通过调用数据库异步操作来解决问题。由于此数据库操作接受回调作为参数,因此您的承诺不太可能通过 undefined 以外的任何方式解决。

确保您正确处理您的 Promise 拒绝并尝试使用 .catch() 以便您可以发现错误,否则在 Promise 中定位代码问题的来源可能会很困难。

【讨论】:

    【解决方案4】:

    再添加一个答案:

    // get current litecoin price from Bitfinex
    const getLtcPrice = (ltc_url) =>
      new Promise(
        (resolve, reject) =>
          request.get(
            ltc_url,
            (error, response, body) =>
              error
                ? reject(error)
                : resolve(JSON.parse(body).ask)
          )
      );
    
    //save current litecoin price to the database
    const saveLtcPrice = (ltc_price) =>
      new Promise(
        (resolve, reject) =>
          db_name.insert(
            { _id: nonce, currency: "Litecoin", price: ltc_price },
            (err, body) =>
              err
                ? reject(err)
                : console.log(" ltc price : " + ltc_price + ", uploaded to the database ")
                  || resolve(body)
        )
      );
    
    const keepGettingPrice = () => {
      const rec =
        p =>
          //if previous saves were not finished then wait for it to finish
          p = p.then(
            //!!!!!!!!!!!!! where does ltc_url come from?
            _ => getLtcPrice(ltc_url)
          ).then(
            saveLtcPrice
          ).then(
            undefined,
            //handle the error
            err=>console.warn("failed one:",err,)
          )
          .then(
            x=>new Promise((r)=>setTimeout(r,2000))
          );
      return rec(Promise.resolve());
    };
    keepGettingPrice();
    

    【讨论】:

    • 请您使用适当的if 语句和块来解决副作用? console.log(…) &amp;&amp; 甚至不起作用。
    • @Bergi 是的,忘记了 console.log 返回未定义。通常只是像这样将它们挤进去进行调试,所以我不需要为函数添加{}
    猜你喜欢
    • 2016-03-02
    • 2017-08-12
    • 2018-08-26
    • 2019-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-26
    • 1970-01-01
    相关资源
    最近更新 更多