“This is my 27th day of participating in the First Challenge 2022. For more details: First Challenge 2022.”

preface

The observer model and the publish and subscribe model, when looking up information, some articles distinguish between the two, and some articles directly say that the two are the same, it is a model.

Similarities and differences

Since called two kinds of names, there must be some differences. First look at the flow chart of the two modes, or more intuitive to see the difference

  • The Observer mode has two objects, the Observer and the Subject. The Observer observes or subscribes to the target. When the target changes, it notifies the Observer of the Fire Event to update it
  • In the Publish/Subscribe mode, an Event Channel is added between the two objects as an intermediate transition. When the target object changes, it does not directly notify the observer, but notifies the observer to make updates through the Event Channel. Therefore, the observer only needs to Subscribe to the message of the Event Channel

From a macro point of view, both of these are relatively suitable for asynchronous programming paradigms, which can realize the one-to-many relationship between the target and the observer (collection dependency), and can inform the observer in batches to make updates when the target changes at some point in the future

Observer pattern paradigm

// Define the observer pattern class
function observe() {
  this.message = {};
}
// Register the observer
observe.prototype.regist = function (type, fn) {
  this.message[type] = fn;
}
// Trigger the observer
observe.prototype.fire = function (type) {
  this.message[type]();
}
Copy the code

Regist registers the event and fires it once by calling the fire method

The practical application

Example: Make a lottery page, slow down with each turn and end up on the winning element

The idea is to start with a random calculation of which element wins, and all you have to do is control the execution of the animation, turning around a number of times, and finally landing on the middle element

In the observer mode, a motion control module is first required to be responsible for the rotation of the rotary table on the lottery page. When the rotary table is detected to rotate once, it will notify the observer and start rotation again

  • Start by simply drawing a lottery page, highlighting the element currently scrolling to
<body>
  <style>/ / element.item {
      width: 50px;
      height: 50px;
      float: left;
      background: red;
      margin-right: 10px;
      text-align: center;
      line-height: 50px; } // Highlight the element currently rolled to.item-on {
      border: 3px solid blue
    }
  </style>
  <div id="app"></div>
</body>
Copy the code
  // Initialize the HTML and draw the page
  function htmlInit(target) {
    for (var i = 0; i < COUNT; i++) {
      var _div = document.createElement('div');
      _div.setAttribute("class"."item");
      _div.innerHTML = i + 1; target.appendChild(_div); _domArr.push(_div); }}Copy the code
  • See that the page renders elements

  • Realize the motion control module
  // Animation executes the function
  function mover(moveConfig) {
    var nowIn = 0; // Current element
    var removeNum = COUNT - 1; // The previous element

    var timer = setInterval(function () {
      if(nowIn ! =0) {
        removeNum = nowIn - 1;
      }
      
      // Clear the style of the previous element
      _domArr[removeNum].setAttribute('class'.'item');
      // Scroll to the element to add styles
      _domArr[nowIn].setAttribute('class'.'item item-on');
      if(moveConfig.moveTime === 0) { // When going to the first element
        clearInterval(timer)
        return
      }
      nowIn++;
      if (nowIn == moveConfig.moveTime) {
        clearInterval(timer);
        if (moveConfig.moveTime == COUNT) { // The last element is not finished yet
          observeOb.fire('finish'); // The notification is running a lap
        }
      }
    }, moveConfig.speed);
  }
  // Animation control
  function moveControll() {
    var final = getFinal();
    var _circle = Math.floor(final / 10.0); / / the total number of laps
    var _runCircle = 0; // The number of laps run
    var stopNum = final % COUNT; // Final stay element
    var _speed = 50; // Initialization speed
    / / run
    mover({
      moveTime: COUNT,
      speed: _speed
    });
    // Register the things you need to do after going around
    observeOb.regist('finish'.function () {
      var _time = 0;
      _speed += 50; // The speed slows down
      _runCircle++;
      if (_runCircle <= _circle) {
        _time = COUNT;
      } else { // Have run the extra laps and end up on the result element
        _time = stopNum;
      }
      / / run
      mover({
        moveTime: _time,
        speed: _speed }); })}Copy the code
  • View the final effect

  • Attach the complete code
  // Define the observer pattern class
  function observe() {
    this.message = {};
  }
  // Register the observer
  observe.prototype.regist = function (type, fn) {
    this.message[type] = fn;
  }
  // Trigger the observer
  observe.prototype.fire = function (type) {
    this.message[type]();
  }

  // Instantiate the current observer
  var observeOb = new observe();

  var _domArr = []; // Store dom objects
  var COUNT = 10 // Number of objects
  var CIRCLE = 4 / / the total number of laps

  // Initialize the HTML and draw the page
  function htmlInit(target) {
    for (var i = 0; i < COUNT; i++) {
      var _div = document.createElement('div');
      _div.setAttribute("class"."item");
      _div.innerHTML = i + 1; target.appendChild(_div); _domArr.push(_div); }}// The final result, a total of several runs
  function getFinal() {
    var _num = Math.random() * 10 + CIRCLE * COUNT;
    return Math.floor(_num, 0);
  }
  // Animation executes the function
  function mover(moveConfig) {
    var nowIn = 0;
    var removeNum = COUNT - 1;

    var timer = setInterval(function () {
      if(nowIn ! =0) {
        removeNum = nowIn - 1;
      }
      
      _domArr[removeNum].setAttribute('class'.'item');
      _domArr[nowIn].setAttribute('class'.'item item-on');
      if(moveConfig.moveTime === 0) { // Go to the first element
        clearInterval(timer)
        return
      }
      nowIn++;
      if (nowIn == moveConfig.moveTime) {
        clearInterval(timer);
        if (moveConfig.moveTime == COUNT) { // The last element is not finished yet
          observeOb.fire('finish'); // The notification is running a lap
        }
      }
    }, moveConfig.speed);
  }
  // Animation control
  function moveControll() {
    var final = getFinal();
    var _circle = Math.floor(final / 10.0); / / the total number of laps
    var _runCircle = 0; // The number of laps run
    var stopNum = final % COUNT; // Final stay element
    var _speed = 50; // Initialization speed
    / / run
    mover({
      moveTime: COUNT,
      speed: _speed
    });
    // Register the things you need to do after going around
    observeOb.regist('finish'.function () {
      var _time = 0;
      _speed += 50; // The speed slows down
      _runCircle++;
      if (_runCircle <= _circle) {
        _time = COUNT;
      } else { // Have run the extra laps and end up on the result element
        _time = stopNum;
      }
      / / run
      mover({
        moveTime: _time,
        speed: _speed
      });
    })
  }
  htmlInit(document.getElementById('app')); / / initialization
  moveControll(); // Perform the animation
Copy the code