首页 > 解决方案 > 如何重构这个命令式 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函数中我需要urlANDhtml作为输入来将记录写入数据库,但是如果我在 之后将此函数放入管道中fetchHtml,我只会得到html. 还有更多 - 管道中的功能应该是一元的。{ url: 'http...', html: '<html>...' }在将它传递给我之前,我应该以某种方式制作对象writeToCahce吗?

2)我想使用 R.either 函数能够readFromCache或者如果那里没有成功,请从网络管道转到我的 fetch (这也会将 html 保存在 db 中)。但是我的缓存读取函数返回 Promise。我可以使用R.pipeP,但它似乎无法使用eitherR.either继续第一个函数并返回 null。似乎它测试了 Promise 本身,因为它是真实值,它给了我对 pipeP 的承诺,它在那里解析为 null(缓存为空))

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

我觉得我做错了什么。很好奇它是怎么做到的

标签: javascriptfunctional-programmingmonadsramda.js

解决方案


我会使用一些来自瓦罐的助手以及闭包的魔力

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))
}

推荐阅读