首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >通过使用函数式folktale2编程javascript,如何优雅地访问以前任务的结果?

通过使用函数式folktale2编程javascript,如何优雅地访问以前任务的结果?
EN

Stack Overflow用户
提问于 2017-09-06 08:24:36
回答 1查看 483关注 0票数 3

一个任务有几个步骤,如果每个步骤的输入仅仅是直接的最后一步,那就很容易了。然而,更多的情况是,某些步骤不仅取决于直接的最后一步。

我可以通过几种方法来完成工作,但最后都是丑陋的嵌套代码,我希望任何人都能帮助我找到更好的方法。

为了演示,我创建了以下类似于签名的示例,流程有3个步骤,如下所示:

  1. 获取数据库连接(() ->任务连接)
  2. 查找帐户(连接->任务帐户)
  3. 创建令牌(Connection -> accountId ->任务令牌)

#第3步不仅依赖于step#2,还依赖于step#1.

下面是使用folktale2进行的jest单元测试

代码语言:javascript
复制
import {task, of} from 'folktale/concurrency/task'
import {converge} from 'ramda'

const getDbConnection = () =>
    task(({resolve}) => resolve({id: `connection${Math.floor(Math.random()* 100)}`})
)

const findOneAccount = connection =>
    task(({resolve}) => resolve({name:"ron", id: `account-${connection.id}`}))

const createToken = connection => accountId =>
    task(({resolve}) => resolve({accountId, id: `token-${connection.id}-${accountId}`}))

const liftA2 = f => (x, y) => x.map(f).ap(y)

test('attempt#1 pass the output one by one till the step needs: too many passing around', async () => {
    const result = await getDbConnection()
        .chain(conn => findOneAccount(conn).map(account => [conn, account.id])) // pass the connection to next step
        .chain(([conn, userId]) => createToken(conn)(userId))
        .map(x=>x.id)
        .run()
        .promise()

    console.log(result) // token-connection90-account-connection90
})

test('attempt#2 use ramda converge and liftA2: nested ugly', async () => {
    const result = await getDbConnection()
        .chain(converge(
            liftA2(createToken),
            [
                of,
                conn => findOneAccount(conn).map(x=>x.id)
            ]
        ))
        .chain(x=>x)
        .map(x=>x.id)
        .run()
        .promise()

    console.log(result) // token-connection59-account-connection59
})

test('attempt#3 extract shared steps: wrong',  async () => {
    const connection = getDbConnection()

    const accountId = connection
    .chain(conn => findOneAccount(conn))
    .map(result => result.id)

    const result = await of(createToken)
    .ap(connection)
    .ap(accountId)
    .chain(x=>x)
    .map(x=>x.id)
    .run()
    .promise()

    console.log(result) // token-connection53-account-connection34, wrong: get connection twice
})
  • attempt#1是正确的,但是我必须传递非常早步骤的输出,直到步骤需要它,如果它跨越许多步骤,它是非常恼人的。
  • attempt#2也是对的,但最终得到了嵌套的代码。
  • 我喜欢attempt#3,它使用一些变量来保存值,但不幸的是,它不能工作。

更新-1-我认为另一种方法是将所有输出置于将通过的状态,但它可能非常类似于attempt#1。

代码语言:javascript
复制
test.only('attempt#4 put all outputs into a state which will pass through',  async () => {
    const result = await getDbConnection()
    .map(x=>({connection: x}))
    .map(({connection}) => ({
        connection,
        account: findOneAccount(connection)
    }))
    .chain(({account, connection})=>
        account.map(x=>x.id)
        .chain(createToken(connection))
    )
    .map(x=>x.id)
    .run()
    .promise()


    console.log(result) //     token-connection75-account-connection75
})

update-2通过使用@Scott的do方法,我对下面的方法相当满意。又短又干净。

代码语言:javascript
复制
test.only('attempt#5 use do co', async () => {
    const mdo = require('fantasy-do')

    const app = mdo(function * () {
        const connection = yield getDbConnection()
        const account =  yield findOneAccount(connection)

        return createToken(connection)(account.id).map(x=>x.id)
    })

    const result = await app.run().promise()

    console.log(result)
})
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-09-06 22:21:50

您的示例可以编写如下:

代码语言:javascript
复制
const withConnection = connection =>
  findOneAccount(connection)
      .map(x => x.id)
      .chain(createToken(connection))

getDbConnection().chain(withConnection)

这类似于您的第二次尝试,但是使用chain而不是ap/lift来消除对后续chain(identity)的需求。如果您愿意的话,这也可以更新为使用converge,尽管我觉得它在这个过程中失去了大量的可读性。

代码语言:javascript
复制
const withConnection = R.converge(R.chain, [
  createToken,
  R.compose(R.map(R.prop('id')), findOneAccount)
])

getDbConnection().chain(withConnection)

还可以对其进行更新,使其看起来类似于使用生成器的第三次尝试。下面的Do函数定义可以被提供某种形式的"do语法“的现有库替换。

代码语言:javascript
复制
// sequentially calls each iteration of the generator with `chain`
const Do = genFunc => {
  const generator = genFunc()
  const cont = arg => {
    const {done, value} = generator.next(arg)
    return done ? value : value.chain(cont)
  }
  return cont()
}

Do(function*() {
  const connection = yield getDbConnection()
  const account = yield findOneAccount(connection)
  return createToken(connection)(account.id)
})
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46070249

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档