关于异步回调函数中跳转到其他页面
发布于 9 年前 作者 wenshiqi0 8004 次浏览 最后一次编辑是 8 年前 来自 问答

我在bearcat查询语句的callback中执行跳转,为什么会报cant set headers after they are sent 这个应该这么解决啊 我路由那些都是用的koa 数据库因为要用sql-mapping 就直接用了bearcat-dao,不知道是不是因为koa的原因呢

33 回复

这次请求已经完成了,也就是说你之前某个地方调用了res.end()。 这多半是万恶的回调造成的逻辑不清的后果,还好node考虑的周到。

看下res.end()都在什么地方被调用了。 查一下有没有并行回调之类的,问题多半出在那里。

@coordcn 我没有在哪里调这个函数 但是我感觉应该室koa内部的愿意 所以我现在想把这个异步改成genrator的 应该怎样才能吧这个有回调函数的异步方法改变成generator形式的呢

@wenshiqi0 的确是koa调用的,koa在你之前调用了res.end()

还是因为你的回调执行顺序造成的,koa的res.end()在你回调执行之前就调用了。

http://www.infoq.com/cn/articles/generator-and-asynchronous-programming/田永强的文章,读完就明白了。

不过这种半调子的协程其实增加了编码和理解的难度,需要捋一捋的,这东西会用(随便找个转换库,转换一下,放到yield后面,yield返回值就是回调的返回值),不代表就真的理解了。javascript协程设计还是挺别扭的,特别是要兼容回调的时候,要整个弄明白了要花点时间的,我个人更喜欢lua提供的协程概念,清晰,简单,不复杂。我现在已经实现了形式同步下的异步,正在思考如何完善并发,这方面工作,fibjs做了一点,但是还是有问题,openresty根本没有碰并发,当然lua也可以通过开新的协程来实现并发,但这种新开协程的做法是有代价的,我更倾向于每个客户连接一个协程,在这个协程上做所有异步调度。

javascript有越来越向杂糅怪胎发展的趋势,基本是为了解决一个复杂的问题,引入了另一个复杂的问题,心很大,但力不济。

@coordcn 还是很谢谢

嘻嘻,试试加个return

@wenshiqi0 问题处理了么?田永强的文章你要仔细看,你要用koa,就必须要自己会转换。

我给一个lua回调转协程的例子给你,你就会明白javascript的半调子协程有多坑爹。很多人是没见过好的,就觉得generator老牛逼了。

fs.readFile('test.txt', 'utf-8', function(err, data)
	print(data)
end)

接下转换函数

function wrap(func)
	local current
	return function(...)
		local args = {...}
		args[#args + 1] = function(...)
			local cur = current
			current  = nil
			coroutine.resume(cur, ...)
		end
		fs.readFile(unpack(args))
		current = coroutine.running()
		coroutine.yield()
	end
end

使用

local readFile = wrap(fs.readFile)
local co = coroutine.create(function()
	local err, data = readFile('test.txt', 'utf-8')
	console.log(data)
end)
coroutine.resume(co)

javascript的协程有这么清晰么?没有。。。

@wenshiqi0 比如 return res.send(data);

@coordcn 我知道nodejs的这个很坑 我最近在学golang goroutine 确实清晰 还在解决中

@hezedu 怎么会有res.send呢 koa不是吧这些都封装了嘛

@wenshiqi0 promise或者thunk,做下转换就可以的。

@wenshiqi0 好吧,我没没用过koa.

@coordcn 我用thunkify加co可以正常的转化callback到generator 但是我用的那个查数据库的函数用的是bearcat 封装了几层 其实就是把原来的函数直接return了过来 这种怎么转化呢

@wenshiqi0

先去掉包装做,想明白了,随便怎么包都一样,关键你取数据的回调在哪里?我个人比较反对javascript搞面向对象,面向对象只在的确需要有继承关系的地方使用,乱用得不偿失。为了面向对象而面向对象的一定是垃圾代码,需要面向对象的地方,c也能面向对象,不需要的地方,除了让代码变长,没有半点好处。

function yourFunOld(name, callback){
	db.get(name, function(err, data){
		if(err) return callback(err);
		callback(err, data);
	});
}

你找到回调调用那个地方,两种方案,一种什么都不变,直接对函数thunk。

另一种其他都不变,去掉callback参数,返回一个包裹回调的函数。

function yourfun(name){
	return function(fn){
		db.get(name,  function(err, data){
			if(err)  return fn(err);
			fn(err, data);
		});
  	};
}

这两种方案,都能使generator.next()返回值{value:, done:} 中的value是一个函数,这个函数就是上面return返回的那个。

var data = yield youfun('name');
var ret = generator.next();
ret.value(function(err, data){
	if(err) throw err;
	generator.next(data);//到这里就传递给了data
});

你们公司就你一个人用node?没人交流么?

继续吐槽es6的设计,能做这样标准出来的都是神人,自己牛逼了不写程序,专门想法子折磨程序员。

我的异步目标是形式同步。 local data, err = db.get(‘name’) 世界多美好,一句话就解决了所有问题,这是货真价实异步的。

@coordcn 我这个小小的实习生就是被抓进去做这个的 公司基本没人用nodejs 他们老一辈不想学这些了 我就去现学现做 我准备明天用promise做 那个包装也不是oo的 只是上头要求这样 不要暴露dao和sql语句 所以需要包装一层

@coordcn 我觉得这样更像并行了 而不是异步 虽然很这么但还是学了很多东西 只要有东西学 我还是非常兴奋的

@wenshiqi0 promise一样能行,没人交流最痛苦了,但只要努力,进步很快的。

他们不学是正确的,很多公司用node,其实都是贪小便宜,性能,前后端通吃,其实都是骗人的,性能node也就那样,两三年前还能吹吹牛逼,现在大家都在一个水平上,比node好的也不少。前后端通吃更是扯蛋,前后端的复杂度不是一个级别的,用前端的写点简单的项目还凑合,稍微复杂点的,靠谱点的都不敢用node。node的这个东西,刚上手给人感觉很好,但用着用着就感觉不对了。

@coordcn 我自己喜欢c一些 顺带也用golang了 以前准备做游戏的还弄了opengl 结果现在去做后端 发现服务器更好玩

@wenshiqi0 你指的是我说的形式同步? 这个不是并行,node严格来说,不存在并行,线程可以算,但前提是分别在不同CPU核心上运行,才算并行。 node所谓并行,其实是并发,命令发出去了,执行还是一个个顺序执行的。node的优势仅仅是发了命令不需要傻等命令执行完毕,在返回之前可以发布其他命令。

形式同步代码跟实质同步代码是一样的,在c层面做了协程调度工作,看起来像同步,实质是异步的。

c层面还是要用回调的,只是做了回调转换罢了。

我已经实现了这个部分工作,tcp模块已经完成,还缺一个connect,争取这个月底前完成,下个月开始做http模块和websocket模块,做完我会写一个聊天程序和游戏程序做示范,然后发布,招募志同道合的一起完善。

@coordcn 我也会来参与看看的

@wenshiqi0 c也是我最喜欢的,很干净,整洁,又足够灵活。lua我也很喜欢,和c结合很紧密,用起来很爽。

javascript也不错,但对es6我很不满意,加了很多莫名其妙的东西,可能我老了,不喜欢折腾了,越来越倾向于c那种很稳定的东西。

@wenshiqi0 代码在我github上,links,现在还很原始,只相当于实现了node的os,process,cluster,net,dns模块。原理很简单,tcp模块看完就知道怎么利用协程将回调转换为形式同步了。这个东西要能用,还要实现,http,websocket,redis,mongodb,mysql,https,tls。还有测试,demo,文档。

我是需求驱动加测试驱动的,不完全是,设计的目标就是能够很方便的实现聊天,游戏服务器。今天做了个聊天服务器的试验,客户端用node写的,服务端用links,发现了一个内存问题,也发现了协程调度问题,当客户端速度很快的发消息的时候,协程调度一开始是比较平均的,但是慢慢就会集中到某个协程上去,最后其他协程就不活跃,就这个协程在发送接收了。这个问题在加大客户端发送间隔后消失,但这个问题如果不解决的话,就有可能造成拒绝服务攻击。

@coordcn 我会去学习学习的 谢谢

@coordcn 就怕看的会比较慢 我自身还在学习中

@wenshiqi0 昨天的协程切换调度问题看来是跟内存错误是一起的,内存错误解决了,调度就正常了。在valgrind里执行可能效率也会降低不少,所以出现了协程的切换调度问题。我看人家的测试,说lua的协程切换是每秒600万次,luajit可以做到2000万次,在切换上应该没问题。

@coordcn 情况有点不一样 实际上上面两层方法里面只有一个return语句 直接把旧函数renturn了 而这个旧函数是bearcat通过id在查找到的 而这个返回的函数中有一个参数为cb 就是callback 我用co里面的promise的处理方法来一直报错说cb不是一个function

@coordcn 不好意思 弄出来了 对的 我自己写错了 谢谢

@coordcn 但是返回的不是查询的值 而是connectionmanager

@coordcn我最后用bluebird来解决了

@wenshiqi0 知道是什么原因了么?可否分享一下,我虽然不用node,但技术交流还是很感兴趣的,技术参考和储备是必须的。

@coordcn 其实也没有什么值得分享的 还是我对异步和异步流程的不熟悉 koa这趟框架背后全部用generrator封装了 全部的异步流程也是用这个来控制的 今天在debugger的时候突然发现了koa源代码外面有一个很大的co(),也就是所有的中间件都被放在了co下面 基本上我们所要处理的异步代码就完全表现的是同步的形式 只是node很多框架还是用的cb的形式 我最后是选择了bluebird的fromnode来将回调转换为promise 我觉得这个bluebird非常不错 我看源代码 基本上对大多数的promise实现都能支持 而且网上说速度也是最快的 使用相当简单 这样就完全去回调了 tj的doc虽然把我看得云里雾里的 但是明白之后确实感叹非常好用 虽然看到之前有人说这种实现太浮夸了 但目前而言我觉得koa吧回调处理的非常好 晚点我想自己实现一个简单的fromnode 不过用还是就用bluebird了

@wenshiqi0 这就是node最大的软肋,不管是callback,还是generator都会让人搞得云里雾里,也就是对程序员不是那么友好(尤其在错误处理和调试上)。世界上有几个像TJ那样的神人呢?我坚持认为好的工具必须是大众化的,简单的,容易被一般人理解的,而不是被个别精英垄断的。你碰到的问题如果有更好异步模型,异步操作能够在同步代码下实现,会简单得多。

node的创立者当初那么反对协程,认为回调才能保证更纯粹的异步模式,可是现在大家都用脚在投票,javascirpt越来越协程化,不管是promise还是generator,都在努力的实现用伪同步来实现(虽然很困难,很别扭),技术必须服务于大众,而不是少数精英的玩具。

形式同步一定会实现,到那个时候,程序员才真正获得了解放。技术也必然朝这个方向发展,通俗来讲,因为人类总是想偷懒,哪怕是一秒钟的思考。用政治经济学来讲就是减少了必要劳动时间,提高了生产力,单位时间内程序员能够创造出更多产品。

回到顶部