Linjiayu6 / FE-Notes

[2020] Front-End Notebook
13 stars 2 forks source link

14 - 函数式编程 #14

Open Linjiayu6 opened 4 years ago

Linjiayu6 commented 4 years ago

概念

compose 函数组合

概念:  c(b(a('......'))) 先执行的是a, b, c
合成:  解决嵌套多层的问题,  compose([a, b, c])
// 同步事件串联起来 a => b => c
var a = str => str.toUpperCase() + '+'
var b = str => str.split('').join('&')
var c = str => str + 'bye'

c(b(a('abc')))

实现1 reduce / reduceRight

function compose (funcs) { 
  // 或 funcs.reduceRight((prev, curr) => (...args) => prev(curr(...args)))
  return funcs.reverse().reduce((prev, curr) => (...args) => prev(curr(...args)))
}
compose([a, b, c])('abc')

实现2 指针 + 递归

function compose (funcs) { // funcs 所有执行函数队列 [a, b, c]
  var pointer = 0 // 指针概念
  var _len = funcs.length
  var result = null
  return function fn (...args) {
    if (pointer === _len) return result

    result = funcs[pointer].apply(this, args) // 当前执行
    pointer += 1 // 指针 + 1
    return fn.call(this, result) // 继续往下面执行
  }
}

compose([a, b, c])('abc')
Linjiayu6 commented 4 years ago

curry 多参数函数 变为 单一参数函数

面试题: 如何实现 multi(2)(3, 4)= 24?

// 接收多参
var add = (a, b, c) => a + b + c

// curry 接收单一参数
var add = a => b => c => a + b + c
var add = function (a) {
  return function (b) {
    return function (c) {
      return a + b + c
    }
  }
}

// 如何实现
var curried = curry(add)
curried(1, 2, 3)()
curried(1, 2)(3)()
curried(1)(2)(3)() 都是一样的结果?

接收参数先保存, 再执行

var add = (...args) => args.reduce((sum, cur) => sum += cur, 0)
function curry (executor) { // executor 最终执行函数
  var _args = [] // 保存函数
  return function fn (...args) {
    if (args && args.length > 0) {
      _args = _args.concat(args) // 接收参数
      return fn // 递归下去
    }
    return executor.apply(this, _args) // 最终执行
  }
}

curry(add)(1, 2)(3)(4, 5)()
curry(add)(1, 2, 3, 4, 5)()
curry(add)(1)(2, 3, 4, 5)()
curry(add)(1)(2, 3, 4)(5)()
Linjiayu6 commented 4 years ago

洋葱模型

class Koa {
  constructor () {
    this.middleware = []
    this.ctx = {}
  }

  use (fn) {
    this.middleware.push(fn)
    return this
  }

  /**
   * 将所有函数组合在一起, 串联流程, 调用next才往下面执行
   * c(b(a()))
   * 先执行a, 通过调用next() koa内部派发 dispatch(i + 1), 继续下一个中间件
   * 目标: 洋葱模型
   */
  compose () {
    var index = 0
    var middleware = this.middleware
    var ctx = this.ctx

    function dispatch (i) {
      if (i >= middleware.length || i < 0) return

      index = i
      var fn = middleware[index]
      var next = () => dispatch(i + 1)
      try {
        return Promise.resolve(fn(ctx, next))
      } catch (err) {
        return Promise.reject(err)
      }
    }

    dispatch(0)
  }

  start () {
    this.compose()
  }
}

var app = new Koa()
app.use(async (ctx, next) => {
  console.log(1)
  await next()
  console.log(6)
})

app.use(async (ctx, next) => {
  console.log(2)
  await next()
  console.log(5)
})

app.use(async (ctx, next) => {
  console.log(3)
  await next()
  console.log(4)
})

app.start()
Linjiayu6 commented 4 years ago

KOA 洋葱模型 + 异步处理

class Koa {
  constructor () {
    this.middleware = []
    this.ctx = {}
  }

  use (fn) {
    this.middleware.push(fn)
    return this
  }

  /**
   * 将所有函数组合在一起, 串联流程, 调用next才往下面执行
   * c(b(a()))
   * 先执行a, 通过调用next() koa内部派发 dispatch(i + 1), 继续下一个中间件
   * 目标: 洋葱模型
   */
  compose () {
    var index = 0
    var middleware = this.middleware
    var ctx = this.ctx

    function dispatch (i) {
      if (i > middleware.length || i < 0) return
      // 若最后一个中间件,返回一个 resolve promise, 必须返回这个要不然报错
      if (i === middleware.length) return Promise.resolve()

      index = i
      var fn = middleware[index]
      var next = () => dispatch(index + 1)
      try {
        return Promise.resolve(fn(ctx, next))
      } catch (err) {
        return Promise.reject(err)
      }
    }

    dispatch(0)
  }

  start () {
    this.compose()
  }
}

var app = new Koa()
app.use(async (ctx, next) => {
  console.log(1)
  await next()
  console.log(8)
})

app.use(async (ctx, next) => {
  console.log(2)
  let p = new Promise((resolve, roject) => {
      setTimeout(() => {
          console.log(3)
          resolve(4)
      }, 3000)
  })
  await p.then(data => console.log(data))
  await next()
  console.log(7)
})

app.use(async (ctx, next) => {
  console.log(5)
  await new Promise(resolve => setTimeout(() => resolve(1), 2000)).then()
  await next()
  console.log(6)
})

app.start()
Linjiayu6 commented 4 years ago

Generator + CO

主动推进

function getData (arg) {
  return new Promise(resolve => setTimeout(() => { resolve(arg + 1) }, arg * 1000))
}

function * generator (a) {
  console.log('1', a)
  var b = yield getData(a)
  console.log('2', b)
  var c = yield getData(b)
  console.log('3', c)
  var d = yield getData(c)
  console.log('4', d)
  return d
}

var g = generator(1)
function doing (...args) {
  var { value, done } = g.next(...args)
  if (done === true) {
    return Promise.resolve(value)
  }
  return value.then(doing)
}
doing().then(console.log)

co结合

function getData (arg) {
  return new Promise(resolve => setTimeout(() => { resolve(arg + 1) }, arg * 1000))
}

function * generator (a) {
  console.log('1', a)
  var b = yield getData(a)
  console.log('2', b)
  var c = yield getData(b)
  console.log('3', c)
  var d = yield getData(c)
  console.log('4', d)
  return d
}

function co (generator, ...args) {
  return new Promise(resolve => {
    var g = generator(...args)

    function executor (...args1) {
      var { value, done } = g.next(...args1)
      if (done === true) return resolve(value)
      return value.then((...args2) => executor(...args2))
    }

    executor()
  })
}

// 必须是 Promise加持 才能 then 下去嘛
co(generator, 1).then(d => console.log('最后结果', d))
Linjiayu6 commented 4 years ago

dpr viewpoint media query bfc http get post cache
last-modified IF modified SINCE etag

Linjiayu6 commented 4 years ago

异步串行处理 pipeline

1. reduce

queue.reduce((prev, next) => prev.then(d => next(d))) // 将上一个值返回传给下一个

var A = args => new Promise(resolve => {
  setTimeout(() => {
    console.log('A', args)
    resolve(1)
  }, 1000)
})

var B = args => new Promise(resolve => {
  setTimeout(() => {
    console.log('B', args)
    resolve(2)
  }, 2000)
})

var C = args => new Promise(resolve => {
  setTimeout(() => {
    console.log('C', args)
    resolve(3)
  }, 3000)
})

var queue = [A, B, C]
function compose (queue, arg) {
  return queue.reduce((prev, next) => prev.then(d => next(d)), Promise.resolve(arg))
}
compose(queue, 0)

2. dispatch

var A = args => new Promise(resolve => {
  setTimeout(() => {
    console.log('A', args)
    resolve(1)
  }, 1000)
})

var B = args => new Promise(resolve => {
  setTimeout(() => {
    console.log('B', args)
    resolve(2)
  }, 2000)
})

var C = args => new Promise(resolve => {
  setTimeout(() => {
    console.log('C', args)
    resolve(3)
  }, 3000)
})

var queue = [A, B, C]
function compose (queue) {
  var _args1 = Array.prototype.slice.call(arguments, 1)

  function dispatch (i) {
    var _args2 = Array.prototype.slice.call(arguments, 1)
    if (i === queue.length - 1) return Promise.resolve(queue[i](..._args2))
    if (i < queue.length) {
      return queue[i](..._args2).then((...d) => dispatch(i + 1, ...d))
    }
  }
  dispatch(0, ..._args1)
}
compose(queue, 0)