What I would like to do – use the "AWAIT" keyword so that I get the results before the program ends and hangs.
I’ve read dozens of blogs, with answers like:
- Use the await keyword, simple
- wrap with async(){}
- and some say await is not needed at all.
but those haven’t not worked yet for me.
const redis = require('ioredis');
const REDIS_RELATIX_HOST = "redis-xxxx.c277.us-east-1-3.ec2.cloud.redislabs.com"
const REDIS_RELATIX_PORT = "12269"
const REDIS_RELATIX_PASSWORD = "mypassword"
let result = "dummy";
const redisClient = redis.createClient({
host: REDIS_RELATIX_HOST,
port: REDIS_RELATIX_PORT,
password: REDIS_RELATIX_PASSWORD
})
redisClient.on('error', err => {
console.log('Error ' + err);
});
await getSpanishWordForRed();
Console.Write("result=" + result);
async function getSpanishWordForRed(){
console.log("getSpanishWordForRed:start")
await redisClient.hget('spanish', 'red',
(err, value) => {
if (err) console.log(err);
else {
console.log(value);
result = value.toString();
}
});
console.log("getSpanishWordForRed:end")
}
console.log("The end") // I've tried with and without this line - just for fun.
I’ve seen posts that say you can just call the async function. But this is what I get.
await getSpanishWordForRed();
^^^^^
SyntaxError: await is only valid in async functions and the top level bodies of modules
This raises two questions:
- am I not in a module?
- am I not in the "top level body"?
So I thought, what if I split my code into two routines, one being a module?
But the issue is I still need the "await" in my main program, don’t I, which is not a module?
I created a main routine:
var mySpanishWord = require('./redisTestNodeOnly8Module.js')
console.log("main before await result");
result = mySpanishWord();
console.log("main result=" + result);
Then I think I created a "module" (filename: redisTestNodeOnly8Module.js):
exports.SpanishWordForRed = async function() {
const redis = require('ioredis');
const REDIS_RELATIX_HOST = "redis-xxxxx.c277.us-east-1-3.ec2.cloud.redislabs.com"
const REDIS_RELATIX_PORT = "12269"
const REDIS_RELATIX_PASSWORD = "mypassword"
const redisClient = redis.createClient({
host: REDIS_RELATIX_HOST,
port: REDIS_RELATIX_PORT,
password: REDIS_RELATIX_PASSWORD
})
redisClient.on('error', err => {
console.log('Error ' + err);
});
let result = "dummy";
console.log("getSpanishWordForRed:start")
await redisClient.hget('spanish', 'red',
(err, value) => {
if (err) console.log(err);
else {
console.log(value);
result = value.toString();
return value.toString();
}
});
}
results:
c:GitHubRelatixAppsCMS_Bad2src>node redisTestNodeOnly8.js
main before await result
getSpanishWordForRed:start
main result=[object Promise]
The end
rojo
^C
This is a shortened version of a question that was already getting long: Redis 4.5.1 – hanging in NodeJS 18
I want to limit this question to just the question of the "module" and "top level" error, and how to get around that. That post shows how I populated the values.
This is the prior thing I tried (redisTestNodeOnly7Module, yes there are six other variations of redis, ioredis, and none working).
const redis = require('ioredis');
const REDIS_RELATIX_HOST = "redis-xxxxx.c277.us-east-1-3.ec2.cloud.redislabs.com"
const REDIS_RELATIX_PORT = "12269"
const REDIS_RELATIX_PASSWORD = "mypassword"
let result = "dummy";
const redisClient = redis.createClient({
host: REDIS_RELATIX_HOST,
port: REDIS_RELATIX_PORT,
password: REDIS_RELATIX_PASSWORD
})
redisClient.on('error', err => {
console.log('Error ' + err);
});
console.log("before call runRedis()");
// wordPromise = await getSpanishWordForRed();
(async function() {
const wordPromise = await getSpanishWordForRed();
//console.log("after call runRedis() wordPromise=" + wordPromise.toString() );
//console.log("after call runRedis() wordPromise=" + wordPromise.resolve());
console.log("after call runRedis() result=" + result);
})();
async function getSpanishWordForRed(){
console.log("getSpanishWordForRed:start")
await redisClient.hget('spanish', 'red',
(err, value) => {
if (err) console.log(err);
else {
console.log(value);
result = value.toString();
}
});
console.log("getSpanishWordForRed:end")
}
console.log("The end")
Results (still shows "the end" before the theoretical "await" happens"):
before call runRedis()
getSpanishWordForRed:start
The end
rojo
getSpanishWordForRed:end
after call runRedis() result=rojo
^C
Update – This is my "copy" of Matt’s code, and the results.
I’m still getting "rojo" back as Spanish word for red, but program still "hangs". If I run "node program.js" it should just end and go back to the command prompt normally? I have to hit CNTL-BREAK to kill it.
const redis = require('ioredis');
const REDIS_RELATIX_HOST = "redis-xxxxx.c277.us-east-1-3.ec2.cloud.redislabs.com"
const REDIS_RELATIX_PORT = "12269"
const REDIS_RELATIX_PASSWORD = "mypassword"
let result = "dummy";
const redisClient = redis.createClient({
host: REDIS_RELATIX_HOST,
port: REDIS_RELATIX_PORT,
password: REDIS_RELATIX_PASSWORD
})
redisClient.on('error', err => {
console.log('Error ' + err);
});
redisClient.on('connect', () => {
console.log('Connected to Redis');
});
async function getSpanishWordForRed(){
console.log("getSpanishWordForRed:start")
return redisClient.hget('spanish', 'red')
}
async function go(){
console.log("go():start")
const result = await getSpanishWordForRed()
console.log("go():end result=", result)
// put the rest of the control flow inside go.
}
// Use this as the commonjs entry, so things are `await`able
go().catch(err => {
console.error("go/catch error:" + err)
process.exit(1)
})
console.log("The end")
Execution:
c:Demosrc>node redisTestNodeOnlyStackOverflowMatt.js
go():start
getSpanishWordForRed:start
The end
Connected to Redis
go():end result= rojo
^C
2
Answers
Matt's answer is correct, as I asked for "ioredis". I earlier had problems with regular "redis" (https://github.com/redis/node-redis) and was having slightly better results with ioredis, but ioredis was causing me problems with webpack5. Here is the same code using Matt's technique with the "redis" module instead of "ioredis".
Note that the "disconnect" is one of the things that kept NodeJs from hanging at the end. This might happen with remote servers more than with a local server (not sure).
I also moved all the redis logic to the go() function and passed the redis object to the getSpanishWordForRed (which could really also be in the go() function).
See also Redis 4.5.1 - hanging in NodeJS 18 on how to solve the issue using modules.
The
modules
term in the errorawait is only valid in async functions and the top level bodies of modules
is referring to an ECMAScript module. The files either need to use the extension.mjs
or be marked as"type": "module"
in thepackage.json
and use theimport
syntax.The codes initial call to
await getSpanishWordForRed()
is "top level" so won’t work in a node Common JS module, using therequire()
syntax. CommonJS is ok though, initiating the async function call a slightly different way is detailed in the example below.For ioredis, the example code doesn’t appear to match the documented connection API. Maybe the hang is related to lack of connection?
The code also mixes together the Promise/await API and the Callback API by supplying a function as the last argument. Just use promises without the callback function:
Using this entry point will remove the top level await/module issues.