菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
490
0

[Javascript] Broadcaster + Operator + Listener pattern -- 10. Define a Function to Set Common Behaviors in Operators

原创
05/13 14:22
阅读数 57198

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

  

发表评论

0/200
490 点赞
0 评论
收藏