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(…) &&
甚至不起作用。
@Bergi 是的,忘记了 console.log 返回未定义。通常只是像这样将它们挤进去进行调试,所以我不需要为函数添加
。以上是关于NodeJS - 对承诺函数的无限调用的主要内容,如果未能解决你的问题,请参考以下文章