タスクにはいくつかのステップがあります。各ステップの入力が直接最後のステップからのものであれば簡単です。しかし、より多くの場合、いくつかのステップは直接最後のステップに依存するだけではありません。folktale2で関数型プログラミングのjavascriptを使用すると、前のタスクの結果に正常にアクセスする方法は?
私はいくつかの方法で解決することができますが、すべてが醜いネストされたコードで終わってしまいます。誰も私がより良い方法を見つけるのを助けてくれることを願っています。 ( - >タスク接続())
- GETデータベース接続:
私は証明するために、次のサインインのような例を作成し、プロセスは以下のように3つのステップがありますアカウント)
- トークン(接続の作成] - > [アカウントID] - > [タスクトークン)
を#のSTEP3は、ステップ#2にばかり依存でなく、#1にステップ。
以下は試み#1が右ですが、私は手順がそれを必要とするまで、それならば、非常に早い段階の出力を渡す必要がfolktale2
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
})
を使用して、冗談ユニットテストですそれは非常に面倒です。
試行#2も正しいですが、ネストされたコードで終わります。
私は試行#3が好きですが、変数を使用して値を保持していますが、残念ながら動作しません。
更新-1 私は通過する状態にすべての出力を配置する別の方法を考えていますが、それかもしれない非常によく似た試み#1
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
})
更新-2 @ Scottのdo
アプローチを使用することで、私は以下のアプローチにかなり満足しています。それは短く清潔です。これはあなたの第二の試みと似て
const withConnection = connection =>
findOneAccount(connection)
.map(x => x.id)
.chain(createToken(connection))
getDbConnection().chain(withConnection)
しかし、その後のchain(identity)
の必要性を取り除くためにchain
ではなくap
/lift
を利用し、次のように
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)
})
私は以前の 'タスク'の結果にアクセスする方法が正しいと思いますか?これを明示的にステートオブジェクトをチェーンに渡すことでこれを行うことはそれほど悪くはありません。さらに、 'task'と' readT'モナド・トランスフォーマー(または、あなたが変異が必要な場合は 'stateT')を組み合わせて、そのオブジェクトから抽象化することができます。しかし、私はこの抽象化が努力する価値があるのかどうか、Javacriptのプロトタイプシステムで適切な 'readerT'を実装できるかどうかはわかりません。 – ftor
@ftor、私は 'readerT'モナドを理解するためにいくつかのhaskellを学ぶ必要があると思います。私は 'readerT'と' stateT'を調べます。この情報に感謝します。 – Ron
関連する、[以前の約束の結果を.then()チェーンにどのようにしてアクセスするのですか?](https://stackoverflow.com/q/28250680/1048572) - 「タスク」は熱心ではないが、モナドインターフェースと 'parallel'の組み合わせは、約束と全く同じです。 – Bergi