简体   繁体   中英

How to refactor this imperative JS to be functional?

I tried all night and could not solve this puzzle. The code, I want to refactor in functional way:

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/')

Problems, that I encountered: 1) In writeToCache function I need url AND html as input to write record to db, but if I put this function in the pipe after fetchHtml , I got only html . And more - functions in pipe should be unary. Should I somehow make object { url: 'http...', html: '<html>...' } before passing that in my writeToCahce ?

2) I wanted to use R.either function to be able to readFromCache OR if no success there, go to my fetch from web pipe (which will save html in db too). But my cache reading function returns Promise. I could use R.pipeP but it seems not working with either ( R.either continues with first function and returns null. Seems like it tests Promise itself and as it is truthy value, it gives my promise to pipeP and it resolves there as null (cache is empty) )

3) I tried to play with Task monad but with not big success. I am still new to these concepts

I feel like I did something completely wrong. Very curious how it can be done

I'd use some helpers from crocks along with the magic of closures

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM