NodeJS中无法在主程序用Await的问题及模块、顶层概念解惑
Node.js中使用await的语法错误与程序挂起问题解决
问题背景
我想在NodeJS主程序里使用await关键字,确保程序结束前获取到异步操作结果,同时避免程序挂起。尝试了直接用await、用async()包裹、不使用await等方案均未成功:
- 初始代码运行触发错误:
SyntaxError: await is only valid in async functions and the top level bodies of modules,产生两个疑问:是否不在模块中?是否不在顶层代码中? - 拆分主程序与模块、尝试立即执行异步函数表达式(IIFE)后,虽能获取Redis返回值,但程序始终挂起,需手动终止。
需要解决:修复await语法错误、明确module和top level概念、解决程序挂起问题。
相关代码
初始代码
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")
拆分后的主程序
var mySpanishWord = require('./redisTestNodeOnly8Module.js') console.log("main before await result"); result = mySpanishWord(); console.log("main result=" + result);
模块文件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(); } }); }
问题解析与解决方案
1. 明确module与top level概念
- Module(模块):Node.js中每个独立的
.js文件都是一个模块,默认采用CommonJS规范(使用require/exports)。若想在顶层代码直接用await,需切换为ES模块:要么将文件后缀改为.mjs,要么在项目的package.json中添加"type": "module"。 - Top Level(顶层代码):指模块文件中,不属于任何函数、代码块(如
if、for)的代码区域。
2. 修复await语法错误
方案一:用IIFE包裹异步逻辑(适合CommonJS模块)
将需要使用await的代码放入立即执行的异步函数中,满足await只能在async函数内使用的规则:
(async () => { // 你的异步逻辑代码 })();
方案二:切换为ES模块
修改package.json添加"type": "module",之后即可在顶层直接使用await,同时需将require/exports替换为ES模块的import/export语法。
3. 解决程序挂起问题
程序挂起的核心原因是Redis客户端未关闭连接,Node.js会等待所有活跃的异步资源释放后才会退出。因此在获取结果后,需调用redisClient.quit()或redisClient.disconnect()关闭连接。
4. 代码中的其他问题修正
- Node.js中控制台输出是
console.log,而非Console.Write(注意大小写); - ioredis的
hget若传入回调函数,返回的不是Promise,此时await不会生效,应使用Promise形式(去掉回调,直接await redisClient.hget(...))。
修正后的代码示例
修正后的初始代码(IIFE方案)
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" (async () => { 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); }); result = await getSpanishWordForRed(redisClient); console.log("result=" + result); console.log("The end"); // 关闭Redis连接,避免程序挂起 await redisClient.quit(); })(); async function getSpanishWordForRed(redisClient){ console.log("getSpanishWordForRed:start") // 使用Promise形式的hget,去掉回调 const value = await redisClient.hget('spanish', 'red'); console.log(value); console.log("getSpanishWordForRed:end") return value.toString(); }
修正后的拆分模块版本
主程序
const { SpanishWordForRed } = require('./redisTestNodeOnly8Module.js'); (async () => { console.log("main before await result"); const result = await SpanishWordForRed(); console.log("main result=" + result); })();
模块文件redisTestNodeOnly8Module.js
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" exports.SpanishWordForRed = async function() { 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("getSpanishWordForRed:start") // 改用Promise形式调用hget const value = await redisClient.hget('spanish', 'red'); console.log(value); // 关闭Redis连接 await redisClient.quit(); return value.toString(); }
内容的提问来源于stack exchange,提问作者NealWalters
相关产品推荐
相关产品推荐

