Learning summary, in order to build wheels to measure the learning effect. The output of this paper is planned after seeing the shadow of Reduce in both Koa-compose and Redux

Reduce implementation

The reduce() method performs a reducer function (in ascending order) that you provide on each element in the array, summarizing its results into a single return value.

The reducer function receives four parameters:

  1. Accumulator (ACC)
  2. Current Value (CUR)
  3. Current Index (IDX)
  4. Source Array (SRC)

The return value from the Reducer function is assigned to the accumulator and is remembered in each iteration of the array and becomes the final single result value.

arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
Copy the code

Implement a Reduce

  Array.prototype.reduce1 = function (callback, initValue) {
    // The array to call
    let curArr = this
    // Last return value
    let returnValue
    / / starting point
    let startIndex

    // Empty array - with an initial value
    if(curArr.length === 0) {
      return initValue
    }

    // Check whether the second argument is passed (initial value)
    returnValue = typeof initValue === 'undefined' ? curArr[0] : initValue
    
    // The initial iteration position
    startIndex = typeof initValue === 'undefined' ? 1 : 0
    
    for (let index = startIndex; index < curArr.length; index++) {
      // Execute callback in turn
      returnValue = callback(returnValue, curArr[index], index, curArr)
    }

    return returnValue
  }
  
  console.log('reduce'[1.2.3].reduce1((pre, cur) = > pre + cur, 1))
  / / 7
Copy the code

Reduce implements the sequential execution of promises

Thinking of sequential execution of promises is, of course, possible with async await. This paper mainly talks about the realization of Reduce.

The runQueuePromise method will be called by an array that returns a Promise for each item, and each Promise in the array will be executed in turn

  const f1 = () = > new Promise((resolve, reject) = > {
    setTimeout(() = > {
      console.log('f1 running')
      resolve(1)})})const f2 = () = > new Promise((resolve, reject) = > {
    setTimeout(() = > {
      console.log('f2 running')
      resolve(1)})})const fnArray = [f1, f2]

  function runQueuePromise(arr, initValue) {
    return arr.reduce((preChain, curFunction) = > preChain.then(curFunction), Promise.resolve(initValue))
  }

  runQueuePromise(fnArray, 'initvalue')

Copy the code

Reduce implementation pipe

Pipe is about piecing together small features to create our own desired features.

Ex. :

const fn1 = x= > x + 1
const fn2 = x= > x + 2
Copy the code

We want to pass a parameter through the first function and then through the second function. The most direct and simplest way is:

f2(f1(1)) / / 4
Copy the code

Write a simple pipe function that returns a new function for the purpose above

Pipe (f1, f2, f3) is a curried function that returns a new function that completes (… args) => f3(f2(f1(… Args))).

const fn1 = x= > x + 1
const fn2 = x= > x + 2

const pipe = (. funcs) = > input= > funcs.reduce(
    (f1, f2) = > f2(f1),
    input
)

const newPipe = pipe(fn1, fn2)
console.log('pipe:', newPipe(1))
Copy the code

Reduce implementation compose

Compose, like pipe, performs a series of tasks (methods), such as:

let funcs = [fn1, fn2, fn3, fn4]
letcomposeFunc = compose(... funcs) composeFunc(args) fn1(fn2(fn3(fn4(args))))// compose
fn1(fn2(fn3(fn4(args))))

// pipe
fn4(fn3(fn2(fn1(args))))
Copy the code

The only difference is the order of execution

1. Process-oriented implementation: recursion

Closure is used to store result and function array length as well as traversal index using closure variable, and use recursive thought to carry out cumulative calculation of results.

function compose(. args) {
  // Function array length. The closure variable functions the array length and traversal index
  let len = args.length
  // Number of functions to be executed: total - 1
  let total = len - 1
  // The closure variable stores the result
  let result

  return function fn(. args1) {
    // Execute the function from right to left
    result = args[total].apply(this, args1)
    // The array of functions is complete
    if (total <= 0) {
      total = len - 1
      return total
    }
    total--
    return fn.call(null, result)
  }
}

Copy the code

2. Implementation of redux version

Take full advantage of the reduce method, my favorite

/ / version of the story
function compose(. funcs) {
    if (funcs.length === 0) {
        return arg= > arg
    }

    if (funcs.length === 1) {
        return funcs[0]}return funcs.reduce((a, b) = > (. args) = >a(b(... args))) }Copy the code

3. Implementation of koa-compose version

function compose (middleware) {
  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array! ')
  for (const fn of middleware) {
    if (typeoffn ! = ='function') throw new TypeError('Middleware must be composed of functions! ')}/ * * *@param {Object} context
   * @return {Promise}
   * @api public* /

  return function (context, next) {
    // last called middleware #
    let index = -1
    return dispatch(0)
    function dispatch (i) {
      if (i <= index) return Promise.reject(new Error('next() called multiple times'))
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      if(! fn)return Promise.resolve()
      try {
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}
Copy the code

So far, reduce has many uses.

PS: Welcome to exchange and study, please point out any shortcomings.