I've created a very simple server using Meteor, to send an email after a timeout. When I use a timeout, the message is successfully sent but an error is thrown: [Error: Can't wait without a fiber]
.
Here's my code:
if (Meteor.isServer) {
Meteor.startup(function () {
// <DUMMY VALUES: PLEASE CHANGE>
process.env.MAIL_URL = 'smtp://me%40example.com:PASSWORD@smtp.example.com:25';
var to = 'you@example.com'
var from = 'me@example.com'
// </DUMMY>
//
var subject = 'Message'
var message = "Hello Meteor"
var eta_ms = 10000
var timeout = setTimeout(sendMail, eta_ms);
console.log(eta_ms)
function sendMail() {
console.log("Sending...")
try {
Email.send({
to: to,
from: from,
subject: subject,
text: message
})
} catch (error) {
console.log("Email.send error:", error)
}
}
})
}
I understand that I could use Meteor.wrapAsync
to create a fiber. But wrapAsync
expects there to be a callback to call, and Email.send
doesn't use a callback.
What should I do to get rid of the error?
This happens because while your Meteor.startup
function runs inside a Fiber (like almost all other Meteor callbacks), the setTimeout
you use does not! Due to the nature of setTimeout
it will run on the top scope, outside the fiber in which you defined and/or called the function.
To solve, you could use something like Meteor.bindEnvironment
:
setTimeout(Meteor.bindEnvironment(sendMail), eta_ms);
And then do so for every single call to setTimeout
, which is a painfully hard fact.
Good thing it's not actually true. Simply use Meteor.setTimeout
instead of the native one:
Meteor.setTimeout(sendMail, eta_ms);
From the docs:
These functions work just like their native JavaScript equivalents. If you call the native function, you'll get an error stating that Meteor code must always run within a Fiber, and advising to use
Meteor.bindEnvironment
Meteor timers just bindEnvironment
then delay the call as you wanted.