오늘의 토끼굴: 이벤트 구동 프로그래밍이 무엇인지, 그리고 자신의 EventEmitter를 어떻게 만드는지

주: 이것은 우선 여정의 이야기입니다.그것은 지도적인 역할을 했지만, 나도 나의 사고 과정과 내가 어떻게 공부했는지 공유하고 싶다.만약 아래의 어떤 것이 완전히 허튼소리라면, 평론에서 나에게 알려주세요!
촉발 요인: 몇 달 전에 나는 자신을 세우라는 요구를 받았다EventEmitter.나는 조금도 모른다. 이것은 매우 난처하다.다음 이야기는 내가 그것에 대한 탐색이다.
나는 한 친구에게 설명했다. 그는 나에게 말했다. ah, you are supposed to build an [EventTarget](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/EventTarget)!.제기랄, 그게 무슨 소리야?!
구글에서 검색what is js eventemitter하고 로그인a nodejs tutorial합니다.
문장을 읽기 몇 줄은 나로 하여금 Javascript Event Loop를 떠올리게 했다. 나는 이미 그것에 관한 책을 많이 읽었다.

Javascript의 이벤트 순환은 무엇입니까?


Lambda 학교에서 우리는 느슨한 경로를 가지고 학생들이 최근의 업무 면접에서 물어볼 수 있는 문제를 공유할 수 있다.나의 성장 심리 상태와 업계에서 진정으로 중요한 것을 배우는 과정에서 나는 이런 문제들을 추적하고 관련 주제를 읽기 시작했다.믿든 안 믿든 네가 맡아라. 내가 미행하는 첫 번째 문제는 What is Javascripts Event Loop, and how does it work?이다.나는 몇 가지 연구를 했고 다음 두 문장을 해결했다.
  • Flavio Copes' The JavaScript Event Loop
  • Sukhjinder Arora's Understanding Asynchronous JavaScript
  • 주요 사상은 Javascript가 단일 라인이라는 것이다.이것은 일이 하나하나 운행되고, 시간이 걸려야 되돌아오는 일은 코드의 집행을 방해한다는 것을 의미한다.Flavio Ilustes가 잘 설명한 바와 같이 이벤트 순환은 끊임없이 검사call stack하며, 그 어떠한 창고와 마찬가지로 후진 선출(LIFO)이다.실행할 함수를 찾으면 스택에 추가합니다
    const bar = () => console.log('bar')
    
    const baz = () => console.log('baz')
    
    const foo = () => {
      console.log('foo')
      bar()
      baz()
    }
    
    foo()
    
    ^ 자료 출처: Flavio Copes article

    ^ 자료 출처: Flavio Copes article
    비동기 코드가 존재할 때 무슨 일이 일어날지.플라비오는 그의 코드에 하나를 추가했다setTimeout():
    const bar = () => console.log('bar')
    
    const baz = () => console.log('baz')
    
    const foo = () => {
      console.log('foo')
      setTimeout(bar, 0)
      baz()
    }
    
    foo()
    
    ^ 자료 출처: Flavio Copes article

    ^ 자료 출처: Flavio Copes article
    이런 상황에서 setTimeOut() 0밀리초 후에 촉발해도 비동기적이다.브라우저 또는 노드js는 타이머를 시작합니다. 타이머가 만료되었을 때, 실행해야 할 코드가 Message Queue라고 불리는 물건에 추가됩니다. 이 물건은 창고의 밑에 있습니다.사용자가 터치한 이벤트 (예를 들어 마우스 클릭) 도 이 대기열에 추가된다는 것을 알았습니다.
    ES6는 Job Queue 에 도입되었습니다.이것은 약속된 결과(API에서 데이터를 얻는 것)가 추가되지 않고 가능한 한 빨리 실행된다는 것을 의미한다Promises.
    here에서 읽은 바와 같이 창고의 작업이 끝날 때마다 노드는 이벤트를 터치하여 이벤트 탐지기에 실행 신호를 보냅니다.이벤트 처리 기반Message Queue.observer patternobserver pattern(출처: Wikipedia.본문은 심지어 JS에서 사용되었다... a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. . 어머, 세상에. RxJS 많다고 들었어요.지금 RXJ를 주차장에 두고 있어요.

    그래, 점원, 그럼 내가 인코딩해야 할 사건 발사기는?


    나는 내가 언급한 first 자원으로 돌아왔다.거기서 읽었어RxJS.이어 Many objects in a Node emit events, for example, a net.Server emits an event each time a peer connects to it, an fs.readStream emits an event when the file is opened. All objects which emit events are the instances of events.EventEmitter.EventEmittermodule 내부에 사는 종류라고 설명했다.
    나는 이 방법들을 이해하기 위해 문장에 열거된 예시로 직접 옮겼다.나를 놀라게 한 것은 이 모든 것이 일리가 있다는 것이다.나는 작은 예를 하나 썼는데, 나는 이 모든 것이 의미가 있어서 매우 기쁘다.이 점에서 저는 제 이벤트를 구축하는 것이 아니라 이런 방법을 연습하고 있습니다.나의 유일한 의문은 어떻게 논점을 청중에게 전달하는가이다.예:
  • 이벤트 이름을 기록 중인 문장에 추가하고 싶습니다.
  • const listener1 = (argument) => {
        console.log(`Hey, I am listener 1 on event ${argument}`);
    };
    
    나는 내가 탐지기를 호출하는 매개 변수를 사용해야 한다고 생각한다.
    emitter.addListener('test', listener1('test');
    
    이것은 잘못을 일으켰다.구글에서 검색해 봤는데 답을 찾았습니다here. 사실은 탐지기를 추가할 때 함수의 이름을 표시했을 뿐 호출하는 것이 아니라는 것을 증명합니다.이벤트를 보낼 때 매개 변수를 전달합니다.이렇게:
    emitter.addListener('test', listener1);
    emitter.emit('test', 'arg1');
    
    만약 우리가 몇 명의 청중이 서로 다른 매개 변수를 기대하고 있다면?이렇게:
    const listener1 = (arg1) => {
        console.log(`Hey, I am listener 1 on event ${arg1}`);
    };
    
    const listener2 = (arg2) => {
        console.log(`Hey, I am listener 2 on event ${arg2}`);
        return 'listener 2';
    };
    
    위의 창고에서 넘쳐나는 답안에서 내 이해는 모든 탐지기가 기대할 수 있는 모든 파라미터를 전달해야 하고, 탐지기 함수에서 모든 가능한 파라미터를 설명해야 한다는 것이다.이렇게:
    const listener1 = (arg1, arg2) => {
        console.log(`Hey, I am listener 1 on event ${arg1}`);
    };
    
    const listener2 = (arg1, arg2) => {
        console.log(`Hey, I am listener 2 on event ${arg2}`);
        return 'listener 2';
    };
    
    실제로 나는 eventarg2 이 필요하지 않다. 왜냐하면 그것은 arg1 이후이기 때문이다. 그러나 나는 listener1 중에서 반드시 그것을 필요로 한다. 그렇지 않으면 listener2 방법에서 전달되는 첫 번째 매개 변수가 될 것이다.그리고 나는 이런 사건을 보냈다arg2.
    emitter.emit('test', 'arg1', 'arg2');
    
    그것은 실제로 here 에서 해석이 있었지만, 나는 나중에야 비로소 보았다.

    좋다이것은 노드가 발생한 결과다.js박스.EventEmitter는 어떻게 구축합니까?


    여기는 어쨌든 내 토끼굴의 목적지야!나는 구글에서 다음과 같은 강좌를 찾았다.
  • How to Create Your Own Event Emitter in JavaScript by Oleh Zaporozhets
  • How to code your own event emitter in Node.js: a step-by-step guide by Rajesh Pillai
  • 나는 즐겁게 읽어서 마침내 나를 실현하는 것을 배웠다emit.내가 파악해야 할 핵심 개념은 다음과 같다.
  • 한 개test가 한 개 또는 여러 개eventEmitter를 발사한다.
  • 하나emitter가 하나 또는 여러 개events를 촉발한다.event는 리셋 함수입니다. listeners를 받았을 때 실행되는 함수입니다.그러나 우선 당신은 listener 또는 event (사람들도 그것을 add 라고 부른다) 이 사건의 청중이 필요합니다.
  • 따라서 개념적으로 이벤트를 registersubscribe에 저장하는 것은 의미가 있다.각각objectemitter에 저장하는 사건도 의미가 있다.이렇게 하면 listener 을 보낼 때, 우리는 대상 내부의 array (이것은 O (1) 이고, 그 안에 저장된 모든 탐지기 (그것은 O (n) 를 순서대로 실행합니다.나는 모든 탐지기가 실행되어야 하기 때문에 O(n)를 개선할 방법이 없다고 생각한다.
    나는 개인적으로 교실에서 항상 재미있다. 나는 대상을 대상으로 프로그래밍하는 것이 초논리적이고 재미있다. 왜냐하면 그 중의 모든 것이 서로 연결되어 있기 때문이다.나는 JS가 순수한 OOP가 아니라는 것을 안다. 왜냐하면 그것은 원형에 기초한 것이기 때문이다.우리 다음에 다시 이야기합시다.

    현재, 우리는 어떻게 이벤트 클래스를 구축합니까?


    나는 Rajesh's article가 매우 좋다는 것을 발견했다. 왜냐하면 그것은 많은 본기 노드를 구축했기 때문이다.jsevent방법(즉EventEmitter,eventEmitter등).

    학급


    우리는 먼저 클래스 구조 함수를 구축한다.
    class EventEmitter {
        constructor() {
            this.events = {};
        }
    }
    
    앞에서 말한 바와 같이 listenerCount() 속성은 하나의 대상이 될 것입니다. 우리는 rawListeners() 액세스 이벤트 탐지기를 사용할 것입니다.

    탐지기 추가


    다음으로 events 방법을 만듭니다.여기에는 this.events[name]addListener (이벤트를 보낼 때 실행할 함수) 두 가지 매개변수가 있습니다.
    addListener(name, listener) {
    // if event name has not yet been recorded in the object (it is not a property of `this.events` yet), we do it and initialise an array
        if (!this.events[name]) {
            this.events[name] = [];
        }
    // we push the `listener` (function) into the array
        this.events[name].push(listener);
    }
    

    에 있다

    name of the eventlistener가 같기 때문에 .on를 다음과 같이 인코딩합니다.
    on(name, listener) {
        return this.addListener(name, listener);
    }
    

    탐지기 삭제


    다음은 addListener 의 그룹에서 탐지기를 삭제하기 위해 .on 를 작성할 수 있습니다.
    removeListener(name, listenerToRemove) {
    // if event name does not exist in `this.events` object, we throw an error because nothing can be removed
        if (!this.events[name]) {
            throw new Error(`Can't remove listener, event ${name} doesn't exist`);
        }
    // we use one of the high order methods (filter) to filter out the listener to be removed from the array
        this.events[name] = this.events[name].filter((listener) => {
            return listener != listenerToRemove;
        });
    }
    

    닫다

    removeListener()과 유사하며this.events[name].on에 해당한다.따라서
    off(name, listenerToRemove) {
        return this.removeListener(name, listenerToRemove);
    }
    

    일단


    이어서 라제이가 어떻게 실현하는지 .off 방법을 읽으면서 많은 것을 배웠습니다.removeListener() 탐지기가 한 번 실행되면 자동으로 삭제됩니다.따라서
    once(name, listener) {
    // we check if event exists in the object, and if not we create an intialise an array
        if (!this.events[name]) {
            this.events[name] = [];
        }
    // we create a wrapper function, which is the one that will be added to the array. This wrapper function executes the listener that we want to add and calls .removeListener
        const onceWrapper = (...arg) => {
            listener(...arg);
            this.removeListener(name, onceWrapper);
        };
    // we push the wrapper function into the array
        this.events[name].push(onceWrapper);
    }
    
    나를 곤혹스럽게 한 것은 내가 처음에 추가하고 싶은 탐지기를 삭제했다는 것이다.아니오, 포장기를 삭제해야 합니다. 왜냐하면 (우리가 사용하는 방법.once 탐지기를 삭제한 것을 기억하십니까?)그렇지 않으면 우리는 그것을 찾을 수 없고, 어떤 내용도 삭제하지 않을 것이다.나는 내가 무엇을 잘못했는지 발견하는 데 시간이 좀 걸렸다.

    보내다


    다음은 인코딩once입니다.Emit에는 강제 매개 변수(이벤트의 이름)가 있고 임의의 매개 변수를 탐지기에 전달할 수 있습니다.이것이 바로 내가 위의 filter 를 사용하는 이유이다. 왜냐하면 우리는 몇 개의 파라미터를 미리 전달할지 모르기 때문이다.아마도 어떤 탐지기는 3개의 파라미터가 필요할 것이다. (이 숫자는 예시일 뿐이다.) 그리고 사건을 기록하는 모든 탐지기 (수조에 추가) 는 이 3개의 파라미터가 뒤에 있지 않도록 이렇게 많은 파라미터를 받을 준비를 해야 한다.내가 틀리지 않으면 너는 논점emit을 전파함으로써 이 점을 실현할 수 있다.
    emit(name, ...data) {
        if (!this.events[name]) {
            throw new Error(`Can't emit an event. Event ${name} does not exist.`);
        }
    
        this.events[name].forEach((cb) => {
            cb(...data);
        });
    }
    
    우선...arg 대상에서 이벤트...args 속성을 찾을 수 없으면 오류를 던집니다.만약 우리가 사건을 찾게 된다면, 우리는 event 수조에서 교체하고, 매개 변수를 전달하는 탐지기를 실행할 것이다.
    나는 그곳에서 몇 가지 실현을 보았는데, 그것들은 마치 매개 변수를 잊어버렸거나, 아마도 내가 무엇을 빠뜨렸을 것이다.어쨌든, 내 것이 효과가 있는 것 같다. 만약 당신이 어떤 잘못을 발견한다면, 평론에서 나에게 알려주세요.

    수신기 계수


    다음으로 name.이것은 매개 변수 (이벤트의 이름) 를 받아들여서 탐지기의 계수 (그룹에 저장된 탐지기) 를 되돌려줍니다.나는 코드는 말하지 않아도 안다고 생각한다.
    listenerCount(name) {
        if (!this.events[name]) {
            this.events[name] = [];
        }
        return this.events[name].length;
    }
    

    원시 탐지기


    내가 인코딩한 마지막 것은 this.events입니다. 이벤트에 등록된 탐지기 그룹으로 되돌아옵니다.비록 이것은 나에게 있어서 가장 믿을 수 없는 이름이지만, 어쨌든 가장 간단한 이름이다. 그것은 단지 수조로 돌아가기만 하면 된다.
    rawListeners(name) {
        return this.listeners[name];
    }
    
    바로 다음과 같습니다. 현재 새로운 forEach 클래스를 실례화하고 이 실례에서 실행 방법:
    const myEmitter = new EventEmitter();
    myEmitter.on('testEvent', handler1);
    myEmitter.on('testEvent2', handler1);
    myEmitter.emit('testEvent', 'hey there');
    myEmitter.emit('testEvent', 'firing event again');
    myEmitter.emit('testEvent', 'and again');
    
    etc.
    
    좋아했으면 좋겠어!만약 당신이 어떤 잘못을 발견하면 평론에서 저에게 알려주세요.

    좋은 웹페이지 즐겨찾기