작업에는 몇 단계가 있습니다. 각 단계의 입력이 직접 마지막 단계에서만 나온다면 쉽습니다. 그러나 더 자주, 일부 단계는 직접 마지막 단계에만 의존하지 않습니다.folktale2에서 함수형 프로그래밍 javascript를 사용하여 이전 작업의 결과에 정상적으로 액세스하는 방법?
여러 가지 방법으로 해결할 수 있지만 모두 못생긴 중첩 코드로 끝납니다. 더 나은 방법을 찾는 데 도움이 될 수 있기를 바랍니다. (-> 작업 연결())
- GET 데이터베이스 연결 : 내가 보여주기 위해 다음의 로그인 절차와 같은 예를 작성하는 과정은 다음과 같은 3 단계가 계정)
- 토큰 (연결을 생성 -> 계정 아이디 -> 작업 토큰)
#의 STEP3 단계 # 2에뿐만 아니라 중요하지만, 더불어 # 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
})
시도 # 1이 옳다 사용하여 농담 단위 테스트하는,하지만 단계가 필요할 때까지 그 경우, 초기 단계의 출력을 통과해야 많은 단계를 거치며 매우 성가시다.
시도 # 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'와'readerT' 모나드 변환기 (또는 당신이 돌연변이가 필요하다면'stateT')와 결합하여 그 객체로부터 추상화 할 수 있습니다. 그러나이 추상화가 노력할 가치가 있는지, Javacript의 프로토 타입 시스템으로 적절한 'readerT'를 구현할 수 있는지에 대해서는 확신 할 수 없습니다. – ftor
@ftor, 나는'readT' 모나드를 이해하기 위해 몇 가지 haskell을 배워야한다고 생각한다. 아마도 중앙 집중화 된 상태를 좋아할 것이다. 나는'readerT'와'stateT'를 살펴볼 것입니다. 이 정보에 대해 감사드립니다. – Ron
관련하여, [이전의 약속 결과를 .then() 체인에 어떻게 액세스합니까?] (https://stackoverflow.com/q/28250680/1048572) - 'Task's가 열심히 아니지만, 모나드 인터페이스와'parallel' 조합은 약속과 정확히 같습니다. – Bergi