NodeJS - 对承诺函数的无限调用

Posted

技术标签:

【中文标题】NodeJS - 对承诺函数的无限调用【英文标题】:NodeJS - infinite call on promise functions 【发布时间】: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 :我实际上没有收到任何错误消息,只是它只运行一次 【参考方案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 返回未定义。通常只是像这样将它们挤进去进行调试,所以我不需要为函数添加

以上是关于NodeJS - 对承诺函数的无限调用的主要内容,如果未能解决你的问题,请参考以下文章

如何承诺此功能-nodejs [重复]

需要帮助在 NodeJS 中使用 Sinon 存根异步 / 等待承诺

用 Jest 模拟基于承诺的请求

对调用承诺返回函数的递归方法进行单元测试

Nodejs 异步承诺队列

使用异步/等待与承诺的区别?