yqthen.js
**写这个流程库的目的不是为了解决异步回调,而是为了最大限度的挖掘js异步模型的优势。将js的长处发挥到极致 **
async/await确实能写出这个好看的代码,但是我这是流程库啊。。。。 我比较和参考的对象是 async或者thenjs 才是啊。 而且我这个库很适合用来写脚本,你总不能写脚本的时候把方法单独拉出来,封装一遍吧。
1.async常用的API在这里都可以进行链式调用,并且API更加友好。 2.这个库在并发处理上有长处,会让写出的程序有一定的性能提升。
例如下面的例子中 你需要打开数据库和读取文件,耗时都为100MS; 如果用下面的promise形式的话,需要200MS才能完成。generator和await也是200MS;
then(()=>{ db.open() }).then(()=>{ fs.readFile() })
如果用这个库的go方法的话 可以并发完成 只需要100MS,go链里的方法都是并发运行,当go链运行结束后会继续运行then链。
then.go(()=>{ db.open() }).go(()=>{ fs.readFile() }).then...
这样简单的处理,程序的性能就拉开了100MS,而且代码也变得更加美观。
测试用例
业务要求 要求:并发读取文件和打开数据库连接。存入数据库都是并发。 读取一个helloJs.text文件,并将内容保存到’DBXXX’数据库中, 在将该数据提交给远程服务器,地址http:XXX。 如果提交成功,在把文件转成行,每一行一条记录,存到DBXXX数据库中。
伪代码: 一般java或php代码的写法
try{
file = readFile('helloJs.text'); //读取文件
db = DB.open('DBXXX'); //打开数据库
db.save(file); //数据库保存数据
request('http:xxx',file); //像服务器提交数据
lines = file.convertLine;//转换成行
for(var i=0;i<lines.length;i++){
db.save(lines[i]); //每行一条数据,保存到数据库
}
}catch(e){
echo e //捕获异常
}
代码貌似没有问题,但是这里读取文件和打开数据库的操作是可以同时运行的, 如果java用实现的话,可能就要使用多线程模型了。
如果用js实现的话,我想大家都会。 但是用这个库写出来的代码绝对是最简洁优美的。 这里的读取文件和打开数据库是并发的。 yqthen
var then = require('yqthen');
var file,db;
then
.go((next)=>{
readFile('helloJs.text',(err,file)=>{ //读取文件
file = file;
next(err);
})
})
.go((next)=>{
DB.open('DBXXX',(err,db)=>{ //打开数据库
db = db;
next(err);
})
})
.then((next)=>{
db.save(file,(err)=>{ //数据库保存数据
next(err);
})
}).then((next)=>{
request('http:xxx',file,(err)=>{ //像服务器提交数据
next(err);
})
})
.each(file.convertLine,(next,line)=>{
db.save(line,(err)=>{ //每行一条记录保存到数据库
next(err);
})
})
.then((next)=>{
callback(null); //操作成功
})
.fail((next,err)=>{
callback(err); //捕获异常
})
这里在特地比较一下async
async.waterfall([
function(callback){
async.parallel([ //并行运行
function(){ //读取文件
readFile('helloJs.text',(err,file)=>{ //读取文件
file = file;
})
},
function(){ //打开数据库
DB.open('DBXXX',(err,db)=>{ //打开数据库
db = db;
})
}
],
function(err, results){
callback(null, fs, db,callback);
});
},
function(fs, db, callback){
request('http:xxx',file,(err)=>{ //像服务器提交数据
callback(null,fs,db);
})
},
function(fs,db, callback){
async.eachSeries(fs.convertLine, function iteratee(line, callback) { //循环并发
db.save(line,(err)=>{ //每行一条记录保存到数据库
callback(err);
})
});
}
], function (err, result) {
console.log(err,result);
});
//我已经被这个嵌套搞崩溃了!!!
传送门: yqthen
虽然用了yqthen框架,但是异步代码还是没有同步代码简洁,这也是没有办法的事,希望js能像 形式同步更近一点。
但是我这里读取文件和打开数据库用go链连接,也就说这两个方法是并发的,是同时运行的。只有当 这两步操作成功后,才会像数据库保存数据,最后在向服务器提交数据。
- 我很非常喜欢node也热爱开源,如果大家觉得还行,请给我一个星星 鼓励一下吧。
@i5ting 谢谢你还是看了啊。我这个是流程库,如果跟async比的话还是有些创新的。例如我把async的 series,eachSeries,waterfall都链起来写了,API更加友好。至bb 应该属于Promise,不是一个东西吧。如果可以 你能简写下bb的伪代码吗?
我觉得再美也美不过async/await吧,如下:
(async function () {
let file = await readFile('helloJs.text'); //读取文件
let db = await DB.open('DBXXX'); //打开数据库
await db.save(file); //数据库保存数据
await request('http:xxx',file); //像服务器提交数据
lines = file.convertLine;//转换成行
for(var i=0;i<lines.length;i++){
await db.save(lines[i]); //每行一条数据,保存到数据库
}
})( );
至于性能,我觉得都是用的nodejs的nonblocking,应该都差不多吧。
@892280082 我觉得用什么都无所谓,关键看是否解决了问题,你的问题主要是异步代码同步化,至于并发,nodejs本来就是non blocking的,promise也好,callback也好,yield/generator也好,async/await也好都是并发的。
async/await 通过babel的transform,已经是production ready了。
@ron-liu 嗯 是的 用什么工具都看个人喜好。 只是我有一点不明白 let file = await readFile(‘helloJs.text’); //读取文件 let db = await DB.open(‘DBXXX’); //打开数据库 async-await的话 不是有顺序的吗 先读取文件 在打开数据库吗? yield/generator 也只是循环调用,也是有先后顺序的吧,需要前面的任务完成在进行下一个任务,也没有并发的效果啊。 假如 读取文件和打开数据库都需要100MS 那不论是async-await还是yield/generator 完成任务都需要200MS吧 如果并发的话应该只用100MS,这就是我写这个库的目的,用最简单的写法,发挥js最大的性能。 我不是钻牛角尖,我只是想搞清楚,是不是我以前理解错了。嘿嘿。
non blocking 只是无锁吧,并发还是需要代码逻辑实现的吧。
@892280082 毕竟Promise与async/await都已或者即将是标准,对于你说的并发
let promises = [
readFile(‘helloJs.text’), // 这里假设这两个方法均返回 Promise<T>
DB.open(‘DBXXX’)
],
let [file, db] = await Promise.all(promises);
这样子也就Ok了,效果很好的
@elrrrrrrr 一个简单的方式每个err都catch,然后返回resolve的promise
let promise = [p1,p2,p3].map(p => p.catch(err => Promise.resolve('Err but ok.')));
Promise.all(promise);