使用Fluture与Ramda

jgr*_*gr0 6 javascript monads ramda.js ramda-fantasy fluture

我正在使用Bluebird做异步操作,但现在必须做很多空/空/错误检查,我不想按照通常的Else路线.我正在考虑使用monad,但尚未彻底完成它.

此外,我希望它能与ramda一起使用,pipe / compose因为我的大多数其他代码都整齐地封装在功能管道中.根据许多 讨论,monadic Futures(Fluture似乎被推荐)比Promises更受欢迎,对pipeP的支持和composeP可能在未来版本中被删除.

Fluture似乎是一个不错的选择,因为据说它可以很好地适应那些坚持幻想 - 土地规格的图书馆(如ramda).

但是我完全迷失了如何实现将Ramda的管道与Fluture集成在一起的东西.我需要一些示例代码的帮助.

例如:

我有一个DB调用,返回一个对象数组.数组可能具有值,为空或未定义.我有一个功能管道,可以转换数据并将其返回到前端.

示例承诺代码:

fancyDBCall1(constraints)
  .then(data => {
    if (!data || data.length === 0) {
      return []
    }
    return pipe(
    ...
    transformation functions
    ...
    )(data)
  })
  .then(res.ok)
  .catch(res.serverError) 
Run Code Online (Sandbox Code Playgroud)

有人可以就一个好的方法提出一些指示.

Fla*_*nix 10

你能做什么 ?

因此,有一些事情可以处理您的代码.但首先,我们来谈谈Monads.

在此代码中,您可以使用3种类型的Monad:

  • 也许(DB可能会返回一些内容,或者nothing)
  • 要么(如果某些数据验证失败,例如)
  • Fluture(取代承诺.Flutures 与承诺不同!)

也许这个,也许不是!

让我们稍微分解你的代码.我们要做的第一件事就是确保你的fancyDBCall1(constraints)退货Maybe.这意味着它可能返回结果,或者什么都不返回.

但是,您fancyDBCall1是异步操作.这意味着它必须返回一个Future.这里的诀窍是让它返回一个值的未来,而不是Future <Array>让它返回一个Future < Maybe Array >.

哇,听起来很复杂的先生!

想想它而不是: Future.of('world');

你有: Future.of( Maybe( 'world' ) );

还不错吧?

这样就可以避免在代码中进行空检查!以下几行将消失:

if (!data || data.length === 0) {
  return []
}
Run Code Online (Sandbox Code Playgroud)

你的例子看起来像:

/*
 * Accepts <Maybe Array>.
 * Most ramda.js functions are FL compatible, so this function
 * would probably remain unchanged.
 **/
const tranform = pipe( .... ); 

// fancyDBCall1 returns `Future <Maybe Array>`
fancyDBCall1(constraints)
  .map( transform )
  .fork( always(res.serverError), always(res.ok) );
Run Code Online (Sandbox Code Playgroud)

看看我们的代码有多好看?但等等,还有更多!

我们要么走得更远,要么我们不走!

所以,如果你密切关注,你知道我错过了一些东西.当然,我们现在处理一个空检查,但如果transform爆炸怎么办?好吧,你会说"我们发送res.serverError".

好.这还算公平.但是,如果transform函数因用户名无效而失败怎么办?

你会说你的服务器炸毁了,但它不是正是如此.您的异步查询很好,但我们得到的数据不是.这是我们可以预料到的,它不像流星撞击我们的服务器场,只是有些用户给了我们一封无效的电子邮件,我们需要告诉他!

这里的诀窍是改变我们的transform功能:

/*
 * Accepts <Maybe Array>.
 * Returns <Maybe <Either String, Array> >
 **/
const tranform = pipe( .... ); 
Run Code Online (Sandbox Code Playgroud)

哇,耶稣香蕉!这个黑魔法是什么?

在这里我们说我们的变换可能返回Nothing或者它返回一个Either.这可能是一个字符串(左侧分支始终是错误)或一个值数组(右侧分支始终是正确的结果!).

把它们放在一起

所以,是的,这次旅行真是太糟糕了,你不是吗?为了给你一些具体的代码让你陷入困境,这里有一些代码与这些结构可能看起来像:

首先,我们有一个Future <Maybe Array>:

const { Future } = require("fluture");
const S = require("sanctuary");

const transform = S.map(
  S.pipe( [ S.trim, S.toUpper ] )
);

const queryResult = Future.of( 
  S.Just( ["  heello", "  world!"] ) 
);

//const queryResult2 = Future.of( S.Nothing );

const execute = 
  queryResult
    .map( S.map( transform ) )
    .fork(
      console.error,
      res => console.log( S.fromMaybe( [] ) ( res ) )
    );
Run Code Online (Sandbox Code Playgroud)

你可以玩queryResultqueryResult2.这应该可以让你很好地了解Maybe monad可以做些什么.

请注意,在这种情况下,我使用的是Sanctuary,它是Ramda的纯粹版本,因为它是Maybe类型,但你可以使用任何Maybe类型库并对它感到满意,代码的想法是相同的.

现在,让我们添加Either.

首先让我们关注我们的转换函数,我稍微修改了一下:

const validateGreet = array =>
  array.includes("HELLO")       ?
  S.Right( array )    :
  S.Left( "Invalid Greeting!" );

// Receives an array, and returns Either <String, Array>
const transform = S.pipe( [
  S.map( S.pipe( [ S.trim, S.toUpper ] ) ),
  validateGreet
] );
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.如果数组服从我们的条件,我们返回带有数组的Either的右分支,而不是带有错误的左分支.

现在,让我们将它添加到我们之前的示例中,它将返回一个Future <Maybe <Either <String, Array>>>.

const { Future } = require("fluture");
const S = require("sanctuary");

const validateGreet = array =>
  array.includes("HELLO")       ?
  S.Right( array )    :
  S.Left( "Invalid Greeting!" );

// Receives an array, and returns Either <String, Array>
const transform = S.pipe( [
  S.map( S.pipe( [ S.trim, S.toUpper ] ) ),
  validateGreet
] );

//Play with me!
const queryResult = Future.of( 
  S.Just( ["  heello", "  world!"] ) 
);

//Play with me!
//const queryResult = Future.of( S.Nothing );

const execute = 
  queryResult
    .map( S.map( transform ) )
    .fork(
      err => {
          console.error(`The end is near!: ${err}`);
          process.exit(1);
      },
      res => {
        // fromMaybe: https://sanctuary.js.org/#fromMaybe
        const maybeResult = S.fromMaybe( S.Right([]) ) (res);

        //https://sanctuary.js.org/#either
        S.either( console.error ) (  console.log ) ( maybeResult )
      }
    );
Run Code Online (Sandbox Code Playgroud)

那么,这告诉我们什么?

如果我们得到异常(没有预料到的东西),我们打印The end is near!: ${err}并干净地退出应用程序.

如果我们的数据库没有返回,我们打印[].

如果DB不返回的东西和事情是无效的,我们打印"Invalid Greeting!".

如果数据库返回正常的东西,我们打印它!

耶稣香蕉,这是很多!

嗯,是的 如果你开始使用Maybe,Either和Flutures,你需要学习很多概念,而且感到不知所措是正常的.

我个人不知道Ramda的任何好的和活跃的Maybe/Either库,(也许你可以尝试Folktale中的Maybe/Result类型?)这就是为什么我使用了Sanctuary,一个来自Ramda的克隆,它更纯粹并且很好地集成了用Fluture.

但是如果你需要从某个地方开始,你可以随时查看社区gitter聊天并发布问题.阅读文档也有很大帮助.

希望能帮助到你!