博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
PlayScala实战 - 如何优雅地取出多层Future中的结果?
阅读量:5820 次
发布时间:2019-06-18

本文共 2343 字,大约阅读时间需要 7 分钟。

hot3.png

1 问题背景

我们先看一下Play中Action代码的基本结构:

def greeting = Action.async { implicit request =>  for{    r1 <- Future.successful("result")    r2 <- Future.successful("result")  } yield {    Ok("hello")  }}

Action的body部分返回类型是Future[Result]。如果只是简单的数据库查询操作,使用for表达式就足够了,就像上面那样。但是如果在yield部分还需要做一些异步的业务处理,问题就出现了,例如下面这个保存商品信息的Action代码:

def doEditProduct(_id: String) = Action.async { implicit request =>  for{    product <- fetchProductAsync    other    <- otherInfoAsync  } yield {    //比较一下,看看哪些信息在页面上被修改了    //...    updateProductAsync()    Redirect(routes.ProductController.editProduct(_id))  }}

首先利用for语句取出异步的product结果,然后对比一下页面数据和数据库中的差异,这一步在很多时候是需要的,例如记录修改日志,然后异步更新至数据库,接着将页面跳转至该商品的编辑页面。那么问题来了,跳转至编辑页面后用户看到的是编辑前的结果还是编辑后的结果?呵呵,只能看运气了!很可能在更新操作未完成之前,编辑页面已经刷出来了。面对这种情况,你很可能会说同步等待updateProductAsync()的结果返回呗,千万别这么干,高并发时你的线程很快就耗尽了,另外updateProductAsync()操作之后可能还会有其它的异步更新操作,就像这样:

def doEditProduct(_id: String) = Action.async { implicit request =>  for{    product <- fetchProductAsync    other    <- otherInfoAsync  } yield {    //比较一下,看看哪些信息在页面上被修改了    //...    updateProductAsync().map{ writeResult =>        if(...){ asyncOperate1() } else { asyncOperate2() }    }    Redirect(routes.ProductController.editProduct(_id))  }}

如果asyncOperate1() 和asyncOperate2()也会更新商品信息, 你可能就要骂娘了...

2 解决方案

其实上面的问题可以归结为本文的标题,即如何从多层Future中取出最终的执行结果。其实for语句可以很容易解决这个问题:

def doEditProduct(_id: String) = Action.async { implicit request =>    val multiLevelsFuture =       for{        product <- fetchProductAsync        other    <- otherInfoAsync      } yield {        updateProductAsync().map{ writeResult =>            asyncOperate1.map{ writeResult1 =>           	 Redirect(routes.ProductController.editProduct(_id))           }        }          }  for{    level1Future <- multiLevelsFuture    level2Future <- level1Future    finalResult  <- level2Future  } yield {  	finalResult  } }

for语句最终会被编译器翻译为map和flatMap,我们索性直接上,而且代码更简洁:

def doEditProduct(_id: String) = Action.async { implicit request =>  (for{    product <- fetchProductAsync    other    <- otherInfoAsync  } yield {    updateProductAsync().map{ writeResult =>        asyncOperate1.map{ writeResult1 =>       	 Redirect(routes.ProductController.editProduct(_id))       }    }      }).flatMap(f1 => f1.flatMap(f2 => f2))    }

注意,for ... yield需要用一对圆括号括起来。转载请注明作者: joymufeng

转载于:https://my.oschina.net/joymufeng/blog/864056

你可能感兴趣的文章
Oracle中的dual
查看>>
Python3安装
查看>>
Openstack的用户登录流程
查看>>
第一章 Web MVC简介
查看>>
as3 加载库声音报错
查看>>
自我介绍
查看>>
无人便利店系统代理发展前景分析
查看>>
git的介绍及使用
查看>>
python-Selenium库的详解
查看>>
百度究竟有没有必要购买PPS?
查看>>
redisi配置安装
查看>>
基础知识(5)- 继承
查看>>
CSharp设计模式读书笔记(23):模板方法模式(学习难度:★★☆☆☆,使用频率:★★★☆☆)...
查看>>
用include()和ob_get_contents( )方法 生成静态文件
查看>>
ekho安装及测试(中文文字转语音)
查看>>
php判断 二维数组中 是否 存在某个一维数组
查看>>
day12 Python字典
查看>>
概率图模型之有向图与无向图之间的关系 I map D map perfect map(完美图) 概念...
查看>>
Python version 2.7 required, which was not found in the registry 问题解决
查看>>
dubbo源码分析11——服务暴露2_doExport()方法分析
查看>>