响应式编程-异步编程的问题与困难
ReactiveX is a library for composing asynchronous and event-based programs by using observable sequences. ? 问题:1、回调地狱;2、逻辑分散; ? 传统方案:回调; 改进方案:promise; 改进方案:monad; 改进方案:rx; ? 异步编程的挑战异步编程的主要困难在于,构建程序的执行逻辑时是非线性的,这需要将任务流分解成很多小的步骤,再通过异步回调函数的形式组合起来。在异步大行其道的 回调函数地狱开头的那个例子使用了4层的嵌套回调函数,如果流程更加复杂的话,还需要嵌套更多,这不是一个好的实践。而且以回调的方式组织流程,在视觉上并不是很直白,我们需要更加优雅的方式来解耦和组织异步流。 使用传统的 var ohs = require(‘./anticorruption/OpenHostService‘); var localConvertingService = require(‘./services/LocalConverting‘); var remoteRepository = require(‘./repositories/BusinessData‘); var calculationService = require(‘./services/Calculation‘); function(req,res) { var userData = req.body; ohs.retrieveResource(userData,ohsCb); function ohsCb(err,rs1) { if(err) { // error handling } localConvertingService.unitize(rs1,convertingCb); } function convertingCb(err,rs2) { if(err) { // error handling } remoteRepository.loadBusinessData(rs2,loadDataCb); } function loadDataCb(err,bs1) { if(err) { // error handling } calculationService.doCalculation(bs1,calclationCb); } function calclationCb(err,result) { if(err) { // error handling } res.view(result); } } 解嵌套的关键在于如何处理函数作用域,之后金字塔厄运迎刃而解。 还有一种更为优雅的 而对于像 public async IActionResult CrazyCase(UserData userData) { var ticket = CrazyApplication.Ticket; var ohsFactory = new OpenHostServiceFactory(ticket); var ohs = ohsFactory.CreateService(); var ohsAdapter = new OhsAdapter(userData); var rs1 = await ohs.RetrieveResource(ohsAdapter); var rs2 = await _localConvertingService.Unitize(rs1); var bs1 = await _remoteRepository.LoadBusinessData(rs2); var result = await _calculationService.DoCalculation(bs1); return View(result); }
异常处理由于异步执行采用非阻塞的方式,所以当前的执行线程在调用后捕获不到异步执行栈,因此传统的异步处理将不再适用。举两个例子: try { Task.Factory.StartNew(() => { throw new InvalidOperationException("diablo coming."); }); } catch(InvalidOperationException e) { // nothing captured. throw; } try { process.nextTick(function() { throw new Error(‘diablo coming.‘); }); } catch(e) { // nothing captured. throw e; } 在这两个例子中, fs.readFile(‘file.txt‘,‘utf-8‘,function(err,data) { }); 其中的 try { await Task.Factory.StartNew(() => { throw new InvalidOperationException("diablo coming."); }); } catch(InvalidOperationException e) { // exception handling. } 编译器所构建的状态机可以支持异常的处理,简直是强大到无与伦比。当然,对于 Task.Factory.StartNew(() => { throw new InvalidOperationException("diablo coming."); }) .ContinueWith(parent => { var parentException = parent.Exception; }); 注意这里访问到的 parentException.Handle(e => { if(e is InvalidOperationException) { // exception handling. return true; } return false; }); 异步流程控制异步的技术也许明白了,但是遇到更复杂的异步场景呢?假设我们需要异步并行的将目录下的3个文件读出,全部完成后进行内容拼接,那么就需要更细粒度的流程控制。 我们可以借鉴async.js这款优秀的异步流程控制库所带来的便捷。 async.parallel([ function(callback) { fs.readFile(‘f1.txt‘,callback) },function(callback) { fs.readFile(‘f2.txt‘,function(callback) { fs.readFile(‘f3.txt‘,callback) } ],function (err,fileResults) { // concat the content of each files }); 如果使用 public async void AsyncDemo() { var files = new []{ "f1.txt","f2.txt","f3.txt" }; var tasks = files.Select(file => { return Task.Factory.StartNew(() => { return File.ReadAllText(file); }); }); await Task.WhenAll(tasks); var fileContents = tasks.Select(t => t.Result); // concat the content of each files } 我们再回到我们开头遇到到的那个场景,可以使用 var ohs = require(‘./anticorruption/OpenHostService‘); var localConvertingService = require(‘./services/LocalConverting‘); var remoteRepository = require(‘./repositories/BusinessData‘); var calculationService = require(‘./services/Calculation‘); var async = require(‘async‘); function(req,res) { var userData = req.body; async.waterfall([ function(callback) { ohs.retrieveResource(userData,rs1) { callback(err,rs1); }); },function(rs1,callback) { localConvertingService.unitize(rs1,rs2) { callback(err,rs2); }); },function(rs2,callback) { remoteRepository.loadBusinessData(rs2,bs1) { callback(err,bs1); }); },function(bs1,callback) { calculationService.doCalculation(bs1,result) { callback(err,result); }); } ],result) { if(err) { // error handling } res.view(result); }); } 如果需要处理前后无依赖的异步任务流可以使用 反人类的编程思维
人类生活在一个充满异步事件的世界,但是开发者在构建应用时却遵循同步式思维,究其原因就是因为同步符合直觉,并且可以简化应用程序的构建。 究其深层原因,就是因为现实生活中我们是在演绎,并通过不同的 同步的思维可以简化编程的关注点,但是没有将流程进行现实化的切分,我们总是倾向于用同步阻塞的方式来将开发变成简单的步骤程序化,却忽视了用动态的视角以及消息/事件驱动的方式构建任务流程。 异步在编程看来是反人类的,但是从业务角度看却是再合理不过的了。通过当的工具及技术,使用异步并不是难以企及的,它可以使应用的资源利用更加的高效,让应用的响应性更上一个台阶。 ? http://www.ituring.com.cn/article/130823 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |