zoukankan      html  css  js  c++  java
  • [Javascript] Broadcaster + Operator + Listener pattern -- 10. Define a Function to Set Common Behaviors in Operators

    In our previous code, we have seen this partten for operators:

    // #region operators
    const concat = curry((broadcaster, listener) => {
      let string = '';
      return broadcaster((value) => {
        if (value === done) {
          listener(done);
          return;
        }
        listener((string += value));
      });
    });
     
    const map = curry((transform, broadcaster, listener) => {
      return broadcaster((value) => {
        if (value === done) {
          listener(done);
          return;
        }
        listener(transform(value));
      });
    });
     
    const filter = curry((predicator, broadcaster, listener) => {
      return broadcaster((value) => {
        if (value === done) {
          listener(done);
          return;
        }
        if (predicator(value)) {
          listener(value);
        }
      });
    });
    // #endregion

    We can create a function to reduce the code:

    const createOperator = curry((operator, broadcaster, listener) => {
    
    })

    let's say, the new function is called 'createOperator', it takes an operator, a broadcaster, and a istener.

    The way we want to use it as:

    const concat = createOperator((broadcaster, listener) => {
      let string = '';
      return broadcaster((value) => {
        listener((string += value));
      });
    });

    As you can notify, we remove the if condition.

    Step1: Strict refoacting:

    const createOperator = curry((operator, broadcaster, listener) => {
      return operator(broadcaster, listener)
    })

    Now, it works almost the same as before, just without if condition check.

    Step2: This time we want to create a new "broadcaster" and invoke the original broadcaster inside new broadcaster.

    1. skeleton

    const createOperator = curry((operator, broadcaster, listener) => {
      // new a new broadcaster and invoke original broadcaster inside new broadcaster
      return operator((behaviorListener) => {
        
      }, listener)
    })

    So what is "behaviorListener": it actual refer to:

    const concat = createOperator((broadcaster, listener) => {
      let string = '';
      return broadcaster((value) => {
        listener((string += value));
      });
    });

    2. invoke the original "broadcaster":

    const _createOperator = curry((operator, broadcaster, listener) => {
      // new a new broadcaster and invoke original broadcaster inside new broadcaster
      return operator((behaviorListener) => {
        return broadcaster((value) => {
          behaviorListener(value)
        })
      }, listener)
    })

    We want to pass the value to "behaviorListener" which refer to the highlighted code.

    Step3: Add common code:

    const _createOperator = curry((operator, broadcaster, listener) => {
      // new a new broadcaster and invoke original broadcaster inside new broadcaster
      return operator((behaviorListener) => {
        // override the default broadcaster
        return broadcaster((value) => {
          // apply common logic
          if (value === done) {
            // stop outer listen to continue emitting values
            listener(done)
            return
          }
          behaviorListener(value)
        })
      }, listener)
    })

    We call "listener(done)" in order to stop the source futhur emtting the values.

    ---

    Put all together:

    const createOperator = curry((operator, broadcaster, listener) => {
      // new a new broadcaster and invoke original broadcaster inside new broadcaster
      return operator((behaviorListener) => {
        // override the default broadcaster
          return broadcaster(value => {
            // apply common logic
            if(value === done) {
              // stop outer listen to continue emitting values
              listener(done)
              return
            }
            // otherwise, we want to pass forward the value to listener
            behaviorListener(value)
          })
      }, listener)
    })

    Step4: Refactoring operators:

    const concat = createOperator((broadcaster, listener) => {
      let string = '';
      return broadcaster((value) => {
        listener((string += value));
      });
    });
    
    const map = transform => createOperator((broadcaster, listener) => {
      return broadcaster((value) => {
        listener(transform(value));
      });
    });
    
    const filter = predicator => createOperator((broadcaster, listener) => {
      return broadcaster((value) => {
        if (predicator(value)) {
          listener(value);
        }
      });
    });

    -- working code example --

    import { curry, compose, toUpper, pipe } from 'ramda';
    
    // #region listeners
    const _log = (value) => console.log(value);
    // #endregion
    
    // #region broadcasters
    const done = Symbol('done');
    const addListener = curry((element, eventType, listener) => {
      return element.addEventListener(evenType, listener);
    });
    const createInterval = curry((time, listener) => {
      let i = 0;
      const id = setInterval(() => {
        listener(i++);
      }, time);
      return () => {
        clearInterval(id);
      };
    });
    const createForOf = curry((iterator, listener) => {
      const id = setTimeout(() => {
        for (let item of iterator) {
          listener(item);
        }
        listener(done);
      }, 0);
      return () => {
        clearTimeout(id);
      };
    });
    const createZipOf = curry((broadcaster1, broadcaster2, listener) => {
      let cancelBoth;
      let buffer1 = [];
      const cancel1 = broadcaster1((value) => {
        buffer1.push(value);
        if (buffer2.length) {
          listener([buffer1.shift(), buffer2.shift()]);
          if (buffer1[0] === done || buffer2[0] === done) {
            listener(done);
            cancelBoth();
          }
        }
      });
    
      let buffer2 = [];
      const cancel2 = broadcaster2((value) => {
        buffer2.push(value);
        if (buffer1.length) {
          listener([buffer1.shift(), buffer2.shift()]);
          if (buffer1[0] === done || buffer2[0] === done) {
            listener(done);
            cancelBoth();
          }
        }
      });
      cancelBoth = () => {
        cancel1();
        cancel2();
      };
      return cancelBoth;
    });
    // #endregion
    
    // #region operators
    const createOperator = curry((operator, broadcaster, listener) => {
      // new a new broadcaster and invoke original broadcaster inside new broadcaster
      return operator((behaviorListener) => {
        // override the default broadcaster
          return broadcaster(value => {
            // apply common logic
            if(value === done) {
              // stop outer listen to continue emitting values
              listener(done)
              return
            }
            // otherwise, we want to pass forward the value to listener
            behaviorListener(value)
          })
      }, listener)
    })
    
    const concat = createOperator((broadcaster, listener) => {
      let string = '';
      return broadcaster((value) => {
        listener((string += value));
      });
    });
    
    const map = transform => createOperator((broadcaster, listener) => {
      return broadcaster((value) => {
        listener(transform(value));
      });
    });
    
    const filter = predicator => createOperator((broadcaster, listener) => {
      return broadcaster((value) => {
        if (predicator(value)) {
          listener(value);
        }
      });
    });
    // #endregion
    const transform =  pipe(
        map((x) => x[1]),
        filter((x) => x !== ','),
        concat,
        map(toUpper)
      );
    let typeGreeting = transform(
      createZipOf(createInterval(100), createForOf('My Zipo'))
    );
    const cancelGreating = typeGreeting(_log)
    // cancelGreating()
    
    const myZip = (broadcaster1, broadcaster2) => (...operators) => {
      return pipe(...operators)(createZipOf(broadcaster2, broadcaster2))
    }
    

      

  • 相关阅读:
    5.18英语
    5.18
    5.17
    单源点最短路模板
    5.16
    mock.js进行接口mock
    docker-compose安装和使用
    docker常用命令
    docker安装和使用(win10家庭版)
    ES6基础(2)-const
  • 原文地址:https://www.cnblogs.com/Answer1215/p/13875493.html
Copyright © 2011-2022 走看看