精华 关于Thunks的一些建议: Thunks和Q的比较
发布于 10 年前 作者 eeandrew 7719 次浏览 最后一次编辑是 8 年前 来自 分享

Thunks 是个好东东。

不过我是个Q的重度使用者,今天对比了一下Thunks和Q,提点建议。

所有thunks正确返回时,输出风格不好

基于promise的代码

var Q = require('q'),
	fs = require('fs'),
	qsize = Q.denodeify(fs.stat);
	
qsize('package.json').then(function(){
	var promises = [];
	promises.push('plain value');
	promises.push(qsize('thunk.js'));
	promises.push(qsize('package.json'));
	promises.push('hello world');
	return promises;
}).then(Q.all)
  .then(console.log)
  .fail(console.log)
  .done();

输出

//一维数组
[ 'plain value',
  { dev: 0,
    mode: 33206,
    nlink: 1,
    uid: 0,
    gid: 0,
    rdev: 0,
    ino: 0,
    size: 358,
    atime: Mon Nov 24 2014 10:06:57 GMT+0800 (China Standard Time),
    mtime: Mon Nov 24 2014 10:44:11 GMT+0800 (China Standard Time),
    ctime: Mon Nov 24 2014 10:06:57 GMT+0800 (China Standard Time) },
  { dev: 0,
    mode: 33206,
    nlink: 1,
    uid: 0,
    gid: 0,
    rdev: 0,
    ino: 0,
    size: 70,
    atime: Mon Nov 24 2014 10:01:35 GMT+0800 (China Standard Time),
    mtime: Mon Nov 24 2014 10:35:46 GMT+0800 (China Standard Time),
    ctime: Mon Nov 24 2014 10:01:35 GMT+0800 (China Standard Time) },
  'hello world' ]

基于Thunks的代码

var Thunks = require('thunks')(),
	fs = require('fs');

var size = Thunks.thunkify(fs.stat);

size('package.json')(function(err,res){
	var thunks = [];
	thunks.push('plain value');
	thunks.push(size('thunk.js'));
	thunks.push(size('package.json'));
	thunks.push('hello world');
	return thunks;
})(Thunks.all)
(function(err,res){
	console.log(res)
})

输出

//没理解包一个数组在外面的意图
[ null,
  [ 'plain value',
    { dev: 0,
      mode: 33206,
      nlink: 1,
      uid: 0,
      gid: 0,
      rdev: 0,
      ino: 0,
      size: 364,
      atime: Mon Nov 24 2014 10:06:57 GMT+0800 (China Standard Time),
      mtime: Mon Nov 24 2014 10:53:55 GMT+0800 (China Standard Time),
      ctime: Mon Nov 24 2014 10:06:57 GMT+0800 (China Standard Time) },
    { dev: 0,
      mode: 33206,
      nlink: 1,
      uid: 0,
      gid: 0,
      rdev: 0,
      ino: 0,
      size: 70,
      atime: Mon Nov 24 2014 10:01:35 GMT+0800 (China Standard Time),
      mtime: Mon Nov 24 2014 10:35:46 GMT+0800 (China Standard Time),
      ctime: Mon Nov 24 2014 10:01:35 GMT+0800 (China Standard Time) },
    'hello world' ] ]

缺少allSettled。当某个thunk出现错误时,不好处理

基于promise的代码

var Q = require('q'),
	fs = require('fs'),
	qsize = Q.denodeify(fs.stat);
	
qsize('package.json').then(function(){
	var promises = [];
	promises.push('plain value');
	promises.push(qsize('notexisted.js'));
	promises.push(qsize('package.json'));
	promises.push('hello world');
	return promises;
}).then(Q.allSettled) 
  .then(console.log)
  .fail(console.log)
  .done();

输出

[ { state: 'fulfilled', value: 'plain value' },
  { state: 'rejected',
    reason: 
     { [Error: ENOENT, stat 'D:\AA\NodeSchool\Thunk\notexisted.js']
       errno: 34,
       code: 'ENOENT',
       path: 'D:\\AA\\NodeSchool\\Thunk\\notexisted.js' } },
  { state: 'fulfilled',
    value: 
     { dev: 0,
       mode: 33206,
       nlink: 1,
       uid: 0,
       gid: 0,
       rdev: 0,
       ino: 0,
       size: 70,
       atime: Mon Nov 24 2014 10:01:35 GMT+0800 (China Standard Time),
       mtime: Mon Nov 24 2014 10:35:46 GMT+0800 (China Standard Time),
       ctime: Mon Nov 24 2014 10:01:35 GMT+0800 (China Standard Time) } },
  { state: 'fulfilled', value: 'hello world' } ]

基于Thunks的代码

var Thunks = require('thunks')(),
	fs = require('fs');

var size = Thunks.thunkify(fs.stat);

size('package.json')(function(err,res){
	var thunks = [];
	thunks.push('plain value');
	thunks.push(size('notexisted.js'));
	thunks.push(size('package.json'));
	thunks.push('hello world');
	return thunks;
})(Thunks.all)
(function(err,res){
	console.log(err)
})

输出

{ [Error: ENOENT, stat 'D:\AA\NodeSchool\Thunk\notexisted.js']
  errno: 34,
  code: 'ENOENT',
  path: 'D:\\AA\\NodeSchool\\Thunk\\notexisted.js' }

thunks链错误处理的思路比较独特

promise代码(promise链)

var Q = require('q'),
	fs = require('fs'),
	qsize = Q.denodeify(fs.stat);
	
qsize('package.json')
.then(function(data){
	console.log(data);
	return qsize('thunk.js')
})
.then(function(data){
	console.log(data)
	return qsize('notexsited.js')
})
.then(function(data){
	console.log(data)
	return qsize('package.json')
})
.then(function(data){
	console.log(data)
})
.fail(function(err){
	console.log(err);
})

输出

//某个节点发生错误时,promise链停止运行。
//可以通过fail统一处理错误 使用方便。比较符合try catch模式的写法
{ dev: 0,
  mode: 33206,
  nlink: 1,
  uid: 0,
  gid: 0,
  rdev: 0,
  ino: 0,
  size: 70,
  atime: Mon Nov 24 2014 10:01:35 GMT+0800 (China Standard Time),
  mtime: Mon Nov 24 2014 10:35:46 GMT+0800 (China Standard Time),
  ctime: Mon Nov 24 2014 10:01:35 GMT+0800 (China Standard Time) }
{ dev: 0,
  mode: 33206,
  nlink: 1,
  uid: 0,
  gid: 0,
  rdev: 0,
  ino: 0,
  size: 394,
  atime: Mon Nov 24 2014 10:06:57 GMT+0800 (China Standard Time),
  mtime: Mon Nov 24 2014 11:11:30 GMT+0800 (China Standard Time),
  ctime: Mon Nov 24 2014 10:06:57 GMT+0800 (China Standard Time) }
{ [Error: ENOENT, stat 'D:\AA\NodeSchool\Thunk\notexsited.js']
  errno: 34,
  code: 'ENOENT',
  path: 'D:\\AA\\NodeSchool\\Thunk\\notexsited.js' }

Thunks代码(thunks链)

var Thunks = require('thunks'),
	fs = require('fs');

var Thunk = Thunks({
	onerror:function(error){console.log(error)}
});
var size = Thunk.thunkify(fs.stat);

size('package.json')(function(err,res){
	console.log(err,res);
	return size('thunk.js');
})(function(err,res){
	console.log(err,res);
	return size('notexsited.js')
})(function(err,res){
	console.log(err,res)
	return size('package.json');
})(function(err,res){
	console.log(err,res)
})

输出

//不太符合try catch模式的写法
null { dev: 0,
  mode: 33206,
  nlink: 1,
  uid: 0,
  gid: 0,
  rdev: 0,
  ino: 0,
  size: 70,
  atime: Mon Nov 24 2014 10:01:35 GMT+0800 (China Standard Time),
  mtime: Mon Nov 24 2014 10:35:46 GMT+0800 (China Standard Time),
  ctime: Mon Nov 24 2014 10:01:35 GMT+0800 (China Standard Time) }
null { dev: 0,
  mode: 33206,
  nlink: 1,
  uid: 0,
  gid: 0,
  rdev: 0,
  ino: 0,
  size: 464,
  atime: Mon Nov 24 2014 10:06:57 GMT+0800 (China Standard Time),
  mtime: Mon Nov 24 2014 11:30:39 GMT+0800 (China Standard Time),
  ctime: Mon Nov 24 2014 10:06:57 GMT+0800 (China Standard Time) }
{ [Error: ENOENT, stat 'D:\AA\NodeSchool\Thunk\notexsited.js']
  errno: 34,
  code: 'ENOENT',
  path: 'D:\\AA\\NodeSchool\\Thunk\\notexsited.js' }

当没有注册错误处理时,Thunks不会抛出错误

promise代码

var Q = require('q'),
	fs = require('fs'),
	qsize = Q.denodeify(fs.stat);
	
qsize('package.json')
.then(function(data){
	console.log(data);
	return qsize('thunk.js')
})
.then(function(data){
	console.log(data)
	return qsize('notexsited.js')
})
.then(function(data){
	console.log(data)
	return qsize('package.json')
})
.then(function(data){
	console.log(data)
})
.done();

输出

程序抛出exception,提醒开发者有错误没有处理

Thunks代码

var Thunks = require('thunks'),
	fs = require('fs');

var Thunk = Thunks();
var size = Thunk.thunkify(fs.stat);

size('package.json')(function(err,res){
	console.log(err,res);
	return size('thunk.js');
})(function(err,res){
	return size('notexsited.js')
})(function(err,res){
	console.log(err,res)
	return size('package.json');
})(function(err,res){
	console.log(err,res)
})
没有异常抛出,开发者可能会忽略掉这个异常
14 回复

@eeandrew 你的写法有问题~,正确写法如下:

size('package.json')(function(err,res){
  return Thunks.all(['plain value', size('thunk.js'), size('package.json'), 'hello world']);
})(function(err,res){
  console.log(res)
})

关于作用域和异常处理的设计,待会儿贴个文章出来,简而言之,比promise高明,用 thunks 的话无需再用 node.js 的 domain 系统。

楼主我也想用Q,有没有中文资料,关于Q api用法的。? 我现在用的bluebird 也是一知半解。另,能不能说说Q和bluebird哪个好?

@winky 我也说不出来哪个好。不过Q的npm下载统计好像是6位数,说明用它的人多点吧。 这里有一个我基于Q的官方文档的教程,你可以看看。

bluebird很好呀,二和一

都说bluebird性能好, 而且很多库可选依赖Promise时也建议bluebird. bluebird出现的晚, 基本功能部分遵循标准, 所以才敢在教程里写 var Promise = require('bluebird');

@eeandrew 谢楼主,我好好看看。

@colder bluebird还有这些故事,涨见识了。

@i5ting 二合一,此话怎讲?

出来啦 https://github.com/thunks/thunks/issues/5

thunks 的作用域和异常处理设计

我认为,thunks 的作用域和异常处理设计是完美的,如果你发现不完美,那一定是 bug,请告知我来修复。

Q 有内存泄露问题(几个月前测试的),速度也慢,如果用 promise,还是建议用 bluebird

另,thenjs 的 benchmark 原来是有 Q 的,因为它老是垫底,加上后来发现的泄露问题,就去除了(请不要叫我 Q 黑)

@zensh 我去 重头学习

@winky 是这样的,它支持generator和promise/A+

回到顶部