因为JavaScript语言异步特性。在使用Node.js运行非常多操作时都会使用到回调函数,当中就包含訪问数据库。假设代码中的业务逻辑略微复杂一点,回调一层层嵌套。那么代码非常easy进入Callback Hell,不管对写代码的人还是阅读代码的人,都是精神上的折磨。

比如对MySQL的一个事务操作,插入一条posts并插入一条log:

var title = 'It is a new post';

connection.beginTransaction(function(err) {
if (err) { throw err; }
connection.query('INSERT INTO posts SET title=?', title, function(err, result) {
if (err) {
return connection.rollback(function() {
throw err;
});
} var log = 'Post ' + result.insertId + ' added'; connection.query('INSERT INTO log SET data=?', log, function(err, result) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
connection.commit(function(err) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
console.log('success!');
});
});
});
});

以上非常简单的一个业务逻辑,已经回调了好几层了,假设略微再复杂一点,那么代码恐怕就无法直视了。

为了防止发生多层嵌套回调的大坑,能够使用Async.js来解决问题。以下来介绍Async.js结合操作MySQL数据库的使用。

async.each批量Insert

假设需求是向log表中插入多条数据。终于返回运行结果,能够使用async.each函数:

var sqls = [
"INSERT INTO log SET data='data1'",
"INSERT INTO log SET data='data2'",
"INSERT INTO log SET data='data3'"
]; async.each(sqls, function(item, callback) {
// 遍历每条SQL并运行
connection.query(item, function(err, results) {
if(err) {
// 异常后调用callback并传入err
callback(err);
} else {
console.log(item + "运行成功");
// 运行完毕后也要调用callback,不须要參数
callback();
}
});
}, function(err) {
// 全部SQL运行完毕后回调
if(err) {
console.log(err);
} else {
console.log("SQL全部运行成功");
}
});

async.each并不能保证运行成功一条SQL语句后再去运行下一条。所以假设有一条运行失败,不会影响到其它语句的运行。

async.eachSeries按顺序批量Insert

假设想要实现运行成功上一条语句后再開始运行数组中下一条语句,能够使用eachSeries函数:

var sqls = [
"INSERT INTO log SET data='data1'",
"INSERT INTO log SET data='data2'",
"INSERT INTO log SET data='data3'"
]; async.eachSeries(sqls, function(item, callback) {
// 遍历每条SQL并运行
connection.query(item, function(err, results) {
if(err) {
// 异常后调用callback并传入err
callback(err);
} else {
console.log(item + "运行成功");
// 运行完毕后也要调用callback,不须要參数
callback();
}
});
}, function(err) {
// 全部SQL运行完毕后回调
if(err) {
console.log(err);
} else {
console.log("SQL全部运行成功");
}
});

async.eachSeries保证了SQL的运行顺序。而且当当中一条运行异常。就不会继续运行下一条。

async.forEachOf获取多条Select语句的查询结果

async.forEachOf相似于async.each,差别是能够接收Object类型參数。而且会在第二个參数回调函数中传入遍历到的每一项的key,更适合批量运行查询语句并返回结果:

var sqls = {
table_a: "select count(*) from table_a",
table_b: "select count(*) from table_b",
table_c: "select count(*) from table_c"
}; // 用于存放查询结果
var counts = {}; async.forEachOf(sqls, function(value, key, callback) {
// 遍历每条SQL并运行
connection.query(value, function(err, results) {
if(err) {
callback(err);
} else {
counts[key] = results[0]['count(*)'];
callback();
}
});
}, function(err) {
// 全部SQL运行完毕后回调
if(err) {
console.log(err);
} else {
console.log(counts);
}
});

运行结果:

{ table_a: 26, table_b: 3, table_c: 2 }

async.map简化获取多条Select语句的查询结果

上面的async.forEachOf获取多条Select语句的查询结果的代码能够使用async.map函数简化成这样:

var sqls = {
table_a: "select count(*) from table_a",
table_b: "select count(*) from table_b",
table_c: "select count(*) from table_c"
}; async.map(sqls, function(item, callback) {
connection.query(item, function(err, results) {
callback(err, results[0]['count(*)']);
});
}, function(err, results) {
if(err) {
console.log(err);
} else {
console.log(results);
}
});

运行结果:

{ table_a: 26, table_b: 3, table_c: 2 }

async.series按顺序运行多条任务

Async.js非常有用的一个功能就是流程控制。回到本文刚開始的那个开启事务运行Insert的样例。每一步都须要上一步运行成功后才干运行,非常easy掉进回调大坑中。

以下有用async.series函数来优化流程控制,让代码更优雅:

var title = 'It is a new post';

// 用于在posts插入成功后保存自己主动生成的ID
var postId = null; // function数组,须要运行的任务列表,每一个function都有一个參数callback函数而且要调用
var tasks = [function(callback) {
// 开启事务
connection.beginTransaction(function(err) {
callback(err);
});
}, function(callback) {
// 插入posts
connection.query('INSERT INTO posts SET title=?', title, function(err, result) {
postId = result.insertId;
callback(err);
});
}, function(callback) {
// 插入log
var log = 'Post ' + postId + ' added';
connection.query('INSERT INTO log SET data=?', log, function(err, result) {
callback(err);
});
}, function(callback) {
// 提交事务
connection.commit(function(err) {
callback(err);
});
}]; async.series(tasks, function(err, results) {
if(err) {
console.log(err);
connection.rollback(); // 错误发生事务回滚
}
connection.end();
});

async.waterfall按顺序运行多条任务而且下一条任务可获取上一条任务的运行结果

上面使用async.series按顺序运行多条任务。可是非常多情况下运行一个任务的时候须要用到上一条任务的相关数据,比如插入一条数据到posts表后,会自己主动生成ID,下一步插入日志会用到这个ID,假设使用async.series函数就须要定义一个变量var postId来存储这个ID,此时可使用async.waterfall来替代async.series。

var title = 'It is a new post';

var tasks = [function(callback) {
connection.beginTransaction(function(err) {
callback(err);
});
}, function(callback) {
connection.query('INSERT INTO posts SET title=?', title, function(err, result) {
callback(err, result.insertId); // 生成的ID会传给下一个任务
});
}, function(insertId, callback) {
// 接收到上一条任务生成的ID
var log = 'Post ' + insertId + ' added';
connection.query('INSERT INTO log SET data=? ', log, function(err, result) {
callback(err);
});
}, function(callback) {
connection.commit(function(err) {
callback(err);
});
}]; async.waterfall(tasks, function(err, results) {
if(err) {
console.log(err);
connection.rollback(); // 错误发生事务回滚
}
connection.end();
});

async.series获取多条SQL的结果

// tasks是一个Object
var tasks = {
table_a: function(callback) {
connection.query('select count(*) from table_a', function(err, result) {
callback(err, result[0]['count(*)']); // 将结果传入callback
});
},
table_b: function(callback) {
connection.query('select count(*) from table_b', function(err, result) {
callback(err, result[0]['count(*)']);
});
},
table_c: function(callback) {
connection.query('select count(*) from table_c', function (err, result) {
callback(err, result[0]['count(*)']);
});
}
}; async.series(tasks, function(err, results) {
if(err) {
console.log(err);
} else {
console.log(results);
}
connection.end();
});

运行结果:

{ table_a: 26, table_b: 3, table_c: 2 }

以上是Async.js操作数据库常常会用到的一些样例,用上它的话就不再须要操心异步回调的坑了。Async.js不只能够用于数据库操作,其它用到异步回调函数的地方都能够使用。比如文件读写等,本文不过通过数据库操作为例来介绍Async.js基本使用方法。它相同也能够运用到其它须要它的地方。除了上面介绍的几个函数外。Async.js还提供了一些其它有用的函数,能够參考文档灵活使用。

Async.js解决Node.js操作MySQL的回调大坑的更多相关文章

  1. .NET程序员也学Node.js——初识Node.js

    清明在石门休了八天假,一眨眼,4月又到中旬了...看到.NET在天朝彻底沦陷而又无能为力,我开始尝试去学习一些新的东西来充实自己,我自然是打死不会去学java的,没有为什么,于是乎,最近开始学习一些前 ...

  2. 如何在vscode里面调试js和node.js

    一般大家调试都是在浏览器端调试js的,不过有些时候也想和后台一样在代码工具里面调试js或者node.js,下面介绍下怎样在vscode里面走断点. 1,用来调试js 一:在左侧扩展中搜索Debugge ...

  3. 利用async和await异步操作解决node.js里面fs模块异步读写,同步结果的问题

    async await 解决异步问题,这两个关键字是es7提出的,所以测试,node和浏览器版本提高一些 async await 操作基于promise实现的 async await这两个关键字是一起 ...

  4. 解决Node.js调用fs.renameSync报错的问题(Error: EXDEV, cross-device link not permitted)

    2014-08-23 今天开始学习Node.js,在写一个文件上传的功能时候,调用fs.renameSync方法错误 出错代码所在如下: function upload(response,reques ...

  5. KoaHub.JS基于Node.js开发的mysql的node.js驱动程序代码

    mysql A node.js driver for mysql. It is written in JavaScript, does not require compiling, and is 10 ...

  6. 解决 Node.js 错误 Error:listen EADDRINUSE

    第一次尝试 node.js 中的 express 框架,写了第一个 js 文件之后,在 WebStorm 运行,到游览器刷新,成功运行. 又创建一个 js 文件,写的是静态路由的访问,结果出现了 Er ...

  7. 极简 Node.js 入门 - Node.js 是什么、性能有优势?

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

  8. Node.js入门-Node.js 介绍

    Node.js 是什么 Node.js 不是一种独立的语言,与 PHP,Python 等"既是语言优势平台"不同,它也不是一个 JavaScrip 框架,不同于 CakePHP,D ...

  9. 浏览器中的 JS 和 Node.js 中的 JS

    一个是前端技术,一个是后端技术 浏览器中的 JavaScript ECMAScript  语言基础,如语法.数据类型结构.一些内置对象 BOM(Browser Object Model)  一些操作页 ...

随机推荐

  1. 被readLine()折腾了一把

    虽然写IO方面的程序不多,但BufferedReader/BufferedInputStream倒是用过好几次的,原因是: 它有一个很特别的方法:readLine(),使用起来特别方便,每次读回来的都 ...

  2. CentOS7重新生成 /boot/grub2/grub.cfg

    CentOS7重新生成 /boot/grub2/grub.cfg CentOS7 is using grub2 and the generated /boot/grub2/grub.cfg rathe ...

  3. [BZOJ1433][luogu_P2055][ZJOI2009]假期的宿舍

    [BZOJ1433][luogu_P2055][ZJOI2009]假期的宿舍 试题描述 输入 输出 输入示例 1 3 1 1 0 0 1 0 0 1 1 1 0 0 1 0 0 输出示例 ^_^ 数据 ...

  4. [canvas入坑1]canvas 画布拖拽效果

    查看效果请到 http://philippica.github.io/  点击drag 和上一篇画图很像,所以有些部分做了省略 当鼠标按下时保存当前画布上的内容到ppImgData中,并且记录下初始点 ...

  5. XWW的难题(bzoj 3698)

    Description XWW是个影响力很大的人,他有很多的追随者.这些追随者都想要加入XWW教成为XWW的教徒.但是这并不容易,需要通过XWW的考核.XWW给你出了这么一个难题:XWW给你一个N*N ...

  6. poj 2441 Arrange the Bulls

    Arrange the Bulls Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 5427   Accepted: 2069 ...

  7. linux之awk手册

    awk 手册   原文 Table of Contents 1. awk简介 2. awk命令格式和选项 2.1. awk的语法有两种形式 2.2. 命令选项 3. 模式和操作 3.1. 模式 3.2 ...

  8. dva脚手架 dva-cli 配置roadhogrc,antd-mobile样式按需加载 不生效的问题

    1.新安装dva-cli脚手架版本0.9.2,dva版本是2.4.1,roadhogrc版本是2.4.9 roadhogrc2 与1 的区别把roadhogrc 改成了webpackrc 所以配置an ...

  9. 原生app与js交互 jsSDK设计

    var UA = window.navigator.userAgent.toLowerCase()var isIOS = UA && /iphone|ipad|ipod|ios/.te ...

  10. Struts2的使用注解配置Action(零配置)

    1.首先引入struts2注解的jar包:struts2-convention-plugin.jar ------------------------------第一种方式-------------- ...