【问题标题】:My react event pooling pseudo example makes sense?我的反应事件池伪示例有意义吗?
【发布时间】:2020-12-05 21:00:43
【问题描述】:

TLDR

我可以检查一下关于 event pooling react 逻辑的实现吗?
我想知道event pooling的原理:)

问题

当我深入研究 react 文档时,我看到了 event pooling
所以,我很好奇event pooling 是什么,我对此进行了研究。

现在我意识到thread pooling。所以它的工作方式类似。所以我做了一些伪event pooling逻辑。
我想知道它是否有意义?

以及任何知道event pooling 实现在react 包中的位置的人。
请给我评论

伪事件池

EventPool 伪实现

class EventPool {
  private static instance: EventPool;
  private taskQueue: Event[] = [];
  private constructor() {
    this.taskQueue = [];
  }

  public static shared() {
    if (!EventPool.instance) {
      EventPool.instance = new EventPool();
    }
    return EventPool.instance;
  }

  enqueue = (event: Event) => {
    this.taskQueue = this.taskQueue.concat(event);
  };

  dequeue = (currentTarget: any) => {
    this.taskQueue = this.taskQueue.filter(
      (event: Event) => event.currentTarget === currentTarget
    );
  };

  clear() {
    // This function called before or after render 
    // (Commit -> Render -> EventPool.shared().clear()) or (Commit -> EventPool.shared().clear() -> Render) 
    this.taskQueue = this.taskQueue.filter((event) => event.isDone === true);
  }
}

关于persist的事件伪实现

class Event {
  persist = () => {
    // This is executed before EventPool.shared.clear
    EventPool.shared().dequeue(this);
  };
}

参考

  1. What is event pooling in react? - StackOverflow
  2. Synthetic Event - React Document
  3. What's the meaning of Event Pooling?
  4. 진보된 쓰레드 풀링 기법 구현 - Korean

【问题讨论】:

  • React 17 removed event pooling,仅供参考...
  • 哦,我明白了,但我只是想知道这个原理!
  • 非常感谢帕特里克·罗伯茨!
  • 你的EventPool 没有意义,不。目的是创建一个预分配的SyntheticEvent 对象池,并重用这些实例以减少垃圾收集的负担。您的EventPool 没有展示这种优化,事实上,使用每个filter()concat() 创建新的taskQueue 数组实例会给垃圾收集带来更多 负担。
  • 哦,我明白了!我只想知道真实事件池的逻辑:0 这只是我的伪代码! filter()concat() 仅用于描述操作

标签: reactjs typescript event-handling pooling


【解决方案1】:

这是SyntheticEvent/EventPool 模式的非常简单的示例。显然在现实生活中,更好地尊重事件的行为会有点复杂,但是这个 sn-p 必须阐明一些概念。

class SyntheticEvent {
    // all the following properties and methods are part of React's
    // synthetic event, but we'll skip it here in favor of simplicity

    // bubbles: boolean
    // currentTarget: DOMEventTarget
    // defaultPrevented: boolean
    // eventPhase: number
    // nativeEvent: DOMEvent
    // preventDefault(): void {}
    // isDefaultPrevented(): boolean { return true }
    // stopPropagation(): void {}
    // isPropagationStopped(): boolean { return true }
    // target: DOMEventTarget
    // timeStamp: number
    // type: string

    // for simplicity we'll consider here only 3 following properties
    isTrusted: boolean
    cancelable: boolean
    persist: () => void

    // this property helps to track status of each synthetic event
    status: 'POOLED' | 'PERSISTED' | 'IN_USE'

    constructor(status, onPersist: () => void) {
        this.status = status;
        this.persist = onPersist;
    }
}

class EventPool {
    private pool: SyntheticEvent[] = [];

    constructor(initialPoolSize: number) {
        // populating pool with pre-allocated events. We will try to re-use
        // them as much as possible to reduce GC load
        for(let i = 0; i < initialPoolSize; i++) {
            this.allocateNewEvent();
        }
    }

    pullEvent(nativeEvent): SyntheticEvent {
        const syntheticEvent = this.getEventFromPool();
        this.populateEvent(syntheticEvent, nativeEvent);
        return syntheticEvent;
    }

    tryPushEvent(syntheticEvent: SyntheticEvent): void {
        if(syntheticEvent.status !== 'PERSISTED') {
            this.clearEvent(syntheticEvent);
        }
    }


    private allocateNewEvent(): SyntheticEvent {
        const newEvent = new SyntheticEvent( 'POOLED', () => {
            newEvent.status = 'PERSISTED';
        });
        this.pool.push(newEvent);
        return newEvent;
    }

    private getEventFromPool() {
        let event = this.pool.find( e => e.status === 'POOLED' );
        if(!event) {
            event = this.allocateNewEvent();
        }

        return event;
    }

    /** Populates synthetic event with data from native event */
    private populateEvent(syntheticEvent: SyntheticEvent, nativeEvent) {
        syntheticEvent.status = 'IN_USE';
        syntheticEvent.isTrusted = nativeEvent.isTrusted;
        syntheticEvent.cancelable = nativeEvent.cancelable;
    }

    /** Sets all previously populated synthetic event fields to null for safe re-use */
    private clearEvent(syntheticEvent: SyntheticEvent) {
        syntheticEvent.status = 'POOLED';
        syntheticEvent.isTrusted = null;
        syntheticEvent.cancelable = null;
    }
}

// Usage
const mainEventPool = new EventPool(2);
smth.onClick = nativeEvent => {
    const syntheticEvent = mainEventPool.pullEvent(nativeEvent);
    userDefinedOnClickHandler(syntheticEvent); // <-- on click handler defined by user
    mainEventPool.tryPushEvent(syntheticEvent);
};

事件池 - React 使用 SyntheticEvent 来封装 本机浏览器事件,以便它们具有一致的属性 不同的浏览器。我们在任何 react-app 中拥有的事件处理程序 实际上是通过 SyntheticEvent 的实例,除非我们使用 nativeEvent 属性来获取底层浏览器事件。

包装原生事件实例可能会导致性能问题,因为 创建的每个合成事件包装器也需要 在某些时候收集的垃圾,这可能是昂贵的 CPU 时间。

React 通过分配一个合成实例池来解决这个问题。 每当触发事件时,它都会从池中获取一个实例并 填充其属性并重用它。当事件处理程序有 完成运行,所有属性将被取消,合成 事件实例被释放回池中。因此,增加 性能。

https://stackoverflow.com/a/53500357/1040070

【讨论】:

  • 哦,我明白了 :) 非常感谢 :)
猜你喜欢
  • 2010-12-31
  • 1970-01-01
  • 2010-09-21
  • 2016-07-06
  • 1970-01-01
  • 2014-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多