如何重构该命令式JS的功能?

时间:2018-09-25 13:40:20

标签: javascript functional-programming monads ramda.js

我整夜都在努力,无法解决这个难题。 我想以功能方式重构代码:

const R = require('ramda')
const axios = require('axios')
const Datastore = require('nedb')

const getDatastore = R.always(
  new Datastore({ filename: './cache.nedb', autoload: true }),
)

const writeHtmlToCache = R.curry((db, url, html) => {
  return new Promise((resolve, reject) => {
    db.insert({ url, html }, (err, doc) => {
      if (err) return reject(err)
      resolve(doc)
    })
  })
})

const readHtmlFromCache = R.curry((db, url) => {
  return new Promise((resolve, reject) => {
    db.findOne({ url }, (err, doc) => {
      if (err) reject(err)
      else resolve(doc)
    })
  })
})

const app = async url => {
  const db = getDatastore()
  let html = R.prop('html', await readHtmlFromCache(db, url))
  if (html) {
    console.log('loaded from cache', html.length)
    return html
  } else {
    html = R.prop('data', await axios.get(url))
    writeHtmlToCache(db, url, html)
    console.log('loaded from web', html.length)
    return html
  }
}

app('http://example.org/')

我遇到的问题: 1)在writeToCache函数中,我需要urlhtml作为将记录写入db的输入,但是如果我将此函数放在fetchHtml之后的管道中,则只会得到{ {1}}。还有-管道中的功能应该是一元的。我应该以某种方式使对象html传递到我的{ url: 'http...', html: '<html>...' }中吗?

2)我想使用R.function来进行writeToCahce的操作,或者如果那里没有成功,请从Web管道中获取内容(这也会将html保存在db中)。但是我的缓存读取功能返回Promise。我可以使用readFromCache,但似乎不能与R.pipeP一起使用(either继续执行第一个函数并返回null。似乎它测试Promise本身,而且它是真实值,这给了我希望到pipeP,并在那里解析为null(缓存为空))

3)我尝试与Task monad一起玩,但没有取得很大的成功。我对这些概念还是陌生的

我觉得自己做错了什么。很好奇如何做到

1 个答案:

答案 0 :(得分:3)

我会使用crocks中的一些辅助功能以及闭包的神奇之处

import unless from 'crocks/logic/unless'
import composeP from 'crocks/combinators/composeP'

const getDataWithUrl = url => () =>
  axios.get(url).then(R.prop('data')))

const writeWithDbAndUrl = (db, url) => html =>
  writeHtmlToCache(db, url, html)

const writeWhenNoHtml = (db, url) =>
  composeP(writeWithDbAndUrl(db, url), getDataWithUrl(url))

const app = url => {
  const db = getDatastore()
  return readHtmlFromCache(db, url)
    .then(unless(R.prop('html'), writeWhenNoHtml(db, url))
}