如何在非async函数中等待Promise?(基于node-mssql)
解决Node.js中MS SQL库阻塞式执行的需求
核心问题
现有项目依赖阻塞式的数据库操作逻辑,但node-mssql返回Promise(异步非阻塞),无法直接适配现有代码;重构整个项目的成本过高,需要找到能让代码等待Promise完成后再返回结果的方案,或者更换支持阻塞执行的MS SQL库。
方案一:用工具将异步Promise转为同步(应急方案)
Node.js本身是异步非阻塞设计,强行同步会破坏其并发特性,但可通过deasync工具实现“伪同步”,仅适合脚本类低并发场景,不推荐生产服务端使用。
示例代码
先安装依赖:
npm install deasync
修改原有类实现:
// 模拟node-mssql的connect方法 const sql = { connect: (config) => new Promise(resolve => { setTimeout(() => resolve({query: (sqlStr) => sqlStr}), 2000) }) } const deasync = require('deasync'); class Foo { db; constructor() { this.db_connect(); } db_connect() { let done = false; let connection; // 发起异步连接,用标记等待完成 sql.connect().then(conn => { connection = conn; done = true; }).catch(err => { done = true; throw err; }); // 轮询事件循环直到连接完成 while (!done) { deasync.runLoopOnce(); } this.db = connection; } db_query(sqlStr) { let done = false; let result; // 发起异步查询,等待结果 this.db.query(sqlStr).then(res => { result = res; done = true; }).catch(err => { done = true; throw err; }); while (!done) { deasync.runLoopOnce(); } return result; } } const foo = new Foo(); console.log(foo.db_query(`SELECT * FROM bar WHERE spam='eggs'`));
方案二:更换支持同步执行的MS SQL库
直接使用原生支持阻塞式调用的库,比如mssql-sync,无需修改原有代码的阻塞逻辑结构。
示例代码
先安装依赖:
npm install mssql-sync
适配原有类:
const sql = require('mssql-sync'); class Foo { db; constructor() { this.db_connect(); } db_connect() { // 同步建立连接,直到完成才返回 this.db = sql.connect({ server: '你的数据库地址', database: '目标数据库', user: '用户名', password: '密码' }); } db_query(sqlStr) { // 同步执行查询,直接返回结果 return sql.query(sqlStr); } } const foo = new Foo(); console.log(foo.db_query(`SELECT * FROM bar WHERE spam='eggs'`));
为什么非async函数里实现await效果这么难?
Node.js基于事件循环实现异步非阻塞,await是Promise的语法糖,依赖async函数的执行上下文来暂停函数、等待Promise完成。非async函数没有这个上下文,无法原生支持暂停执行——强行模拟只能通过轮询事件循环的hack方式,这不仅繁琐,还会阻塞整个事件循环,影响并发能力,因此官方并不推荐这种用法。
内容的提问来源于stack exchange,提问作者droptix
相关产品推荐
相关产品推荐

