자바스크립트는 너의 눈물을 좋아해.

비디오:

  • 자바스크립트는 너의 눈물을 좋아해.


    이것은 내가 JavaScript의 동기화 및 비동기화 행위에 대해 몇 차례 강연한 것이다.실제 통화는 일종의 반실시간 인코딩 통화다.강연이 끝난 후 실제 코드를 사람들에게 남겨 두는 것이 긴장되어 이 글을 만들었다.
    Single-Threaded and Asynchronous JavaScript?

    교란판


    JavaScript의 핵심은 동기화, 블록 분리, 단일 스레드 언어입니다.이것은 한 번에 한 가지 일만 발생할 수 있다는 것을 의미한다.
    사람들이 자바스크립트를 비동기적인 언어라고 말할 때, 그들의 뜻은 당신이 비동기적인 행동을 실현하기 위해 자바스크립트를 조종할 수 있다는 것이다.
    비동기식 경계
  • 사용자 상호 작용
  • 네트워크 IO
  • 디스크 IO
  • 프로세스 간 통신
  • 타이머
  • 정의


    병행성: 다중 스레드 처리와 스케줄링 (동시).
    비동기: 단일 스레드와 이벤트 순환.

    관리인


    병발성: 같은 시간 내에 발생할 수 있는 더 높은 단계의 임무.
    논쟁: 여러 가지 일이 동시에 발생해야 한다.

    JavaScript 엔진 세부 정보


    이것은 JS 엔진의 일부분이 아니다.브라우저 또는 NodeJS 런타임 환경에 포함됩니다.
  • 설정 시간 초과
  • 이벤트 순환
  • Web API
  • 메시지 대기열
  • 작업 대기열
  • 다음은 JavaScript의 호출 스택/이벤트 순환/리셋 대기열과 상호작용하는 방법에 대한 시각적 결과입니다Loupe.
    시간 초과 설정(4ms 지연);MDN setTimeout의 이 글을 참조하십시오.

    In modern browsers, setTimeout()/setInterval() calls are throttled to a minimum of once every 4ms when successive calls are triggered due to callback nesting (where the nesting level is at least a certain depth), or after certain number of successive intervals. - MDN


    지옥으로 돌아가다


    마지막 피라미드:
  • 문제의 증상이지 진정한 문제가 아니다.
  • 반전 제어:
  • 리셋을 통과할 때의 신뢰점...그것의 행위가 기대에 부합되는지 여부.
  • 합리적:
  • 시간 의존은 끼워넣기를 필요로 한다.비선형 사고.
  • 주의사항


    데이터 조정:
  • , 크로스 리셋으로 데이터를 추적합니다.
  • 분할 콜백:
  • 성공과 오류를 구분합니다.
  • 먼저 오류:
  • 분할 리셋과 같은 신뢰 문제.
  • 맹세합니다.
  • 유량 제어 스타일이 좋지 않다.
  • 수직 링크가 아닌 중첩 약속.
  • 다음 코드는 무엇을 합니까?


    export class ThoughtExercise {
      _time = 10000;
      _wrapper = null
    
      constructor() {};
    
      changeColor = () => {
        this._wrapper = document.getElementById('thought-wrapper');
        this._wrapper.style.backgroundColor = 'red';        
      };
      changeLayout = () => {
        let p = document.createElement('p');
        p.setAttribute('id', 'thought-run');
        p.innerText = 'Thought Exercise ...';
        this._wrapper.appendChild(p);
      };
      wait = () => {
        const start = Date.now();
        while(Date.now() < start + this._time) {};
      };
      event = () => {
        this.changeColor();
        this.changeLayout();
        this.wait();
      };
    
      start = () => {
        const button = document.getElementById('thought-button');
        button.classList.remove('hide');
        button.addEventListener('click', this.event);
      };
    }
    
    

    회답


    이 코드는 본질적으로 단추를 연결합니다. 이 단추를 눌렀을 때changeColor,changeLayout,wait 함수를 자극합니다.
    버튼을 클릭하면 코드가 스레드에 잠깁니다. 이때까지_시간은 이미 지나갔다.10000ms가 지나기 전까지는 배경색이 변하지 않습니다.

    비동기식 코드


    다음 코드를 지정합니다...
    
    export class NonAsynchronous {
      _numbers = [1, 2, 3];
    
      constructor() {};
    
      forEachSync = (items, callback) => {
        for (const item of items) {
          callback(item);
        }
      };
    
      forEachAsync = (items, callback) => {
        for (const item of items) {
          setTimeout(() => {
            callback(item);
          }, 0, item);
        }
      };
    
      runSync = () => {
        console.log('The Start');
        this.forEachSync(this._numbers, (number) => {
          console.log(number * 2);
        });
        console.log('The End');
      };
    
      runAsync = () => {
        console.log('The Start');
        this.forEachAsync(this._numbers, (number) => {
          console.log(number * 2);
        });
        console.log('The End');
      };
    
      start = (async = false) => {
        if (!async) {
          this.runSync();
        } else {
          this.runAsync();
        }
      } 
    }
    
    
    기본적으로 여기서 두 가지 다른 **시작이 발생할 수 있습니다: 비비동기와 비동기;각각 runSync*와 runAsync의 실행 옵션이 있습니다.각 실행은 연관된 forEach 기능으로 사용됩니다.
    이걸로runSync 자극, 우리는 콘솔에서 아래의 내용을 보아야 한다...

    ***이거.runAsync 자극, 우리는 아래의 내용을 보아야 한다...

    이곳의 차이를 주의하십시오.동기화가 실행될 때, 모든 것은 우리가 예상한 순서에 따라 발생한다.비동기식으로 실행할 때 콘솔의 숫자는 정상적인 JavaScript 실행 흐름 외부에 표시됩니다.

    단순 네트워크(아날로그)


    답조
  • 응용의 틈새를 관통한다.일부 부트는 필요할 때 완전하지 않을 수 있습니다.
  • 논리적 오류를 처리하다.
  • 이해하기 어려워: 끼워넣기, 호출, 중복 호출, 동기화 호출(막힘)
  • 단순 네트워크, 시뮬레이션 10초 초과.
    export class SimpleNetwork {
      _time = 10000;
    
      constructor() {};
    
      networkRequest = () => {
        setTimeout(() => {
          console.log(`Async Code after ${this._time}ms.`);
        }, this._time);
      };
      start = () => {
        console.log('The Start');
        this.networkRequest();
        console.log('The End');
      };
    };
    
    이 코드에서 우리는 기본적으로 네트워크 요청을 모의했다.setTimeout을 사용하여 10초 지연을 제공합니다.네트워크 요청이 정상적인 JavaScript 실행 흐름 밖에서 완료되는 것을 보아야 합니다...

    이 코드를 실행할 때 마지막 줄은 실제로 10초 지연된 후에 표시됩니다.

    복잡한 네트워크(아날로그)


    중첩 시간 초과 시뮬레이션을 통해 복잡한 네트워크.
    export class ComplexNetwork {
      _time = 0;
    
      constructor() {};
    
      first = () => {
        setTimeout(() => {
          console.log('2');
          this.second();
          console.log('4');
        }, this._time);
      };
    
      second = () => {
        setTimeout(() => {
          console.log('3');
        }, this._time);
      };
    
      start = () => {
        console.log('1');
        this.first();
        console.log('5');
      };
    }
    
    이 시뮬레이션을 검사할 때, 우리가 기대해야 할 것은 순서가 1, 5, 2, 4, 3이어야 한다는 것이다.

    ES2015 간단한 약속


    완료 및 오류 이벤트 처리 제어 반전 문제.
    승낙 신탁
  • 한 번만 해결
  • 성공 또는 오류
  • 메시지 전달/보존
  • 오류가 발생했습니다
  • 해석 후 변경할 수 없음
  • 시간 초과와 약속이 있는 간단한 비동기.질문:
  • 전달값
  • 중첩 문법
  • 고장 처리...
  • 약속, 미래, 연기라고도 한다.
    export class SimplePromise {
      _time = 0;
    
      constructor() {}
    
      timeout = () => {
        setTimeout(() => {
          console.log('setTimeout Fired');
        }, this._time);    
      };
    
      promise = () => {
        new Promise((resolve, reject) => {
          resolve('Resolved');
        })
        .then(res => console.log(res))
        .catch(err => console.log(err));    
      };
    
      start = () => {
        console.log('The Start');
    
        this.timeout();
        this.promise();
    
        console.log('The End');
      };
    }
    
    이 코드는'시작'을 터치한 다음 시간 초과와 약속 함수를 터치하고 마지막에'끝'을 터치해야 합니다.이 경우 작업 순서는 시작이고 끝을 표시해야 합니다.promise가 즉시 해결되고 정상적인 JavaScript 실행 흐름을 초과하는 내용이 없기 때문에 다음 페이지에 표시해야 합니다.마지막으로 시간 초과 기능이 표시됩니다.

    ES2015 복합 약속


    시간 초과와 링크 약속이 있는 복잡한 비동기식.
  • 모듈식으로 가독성이 강하지만 신뢰성이 떨어진다.
  • export class ComplexPromise {
      _time = 0;
    
      constructor() {}
    
      timeout = () => {
        setTimeout(() => {
          console.log('setTimeout Fired');
        }, this._time);    
      };
    
      promise1 = () => {
        return new Promise((resolve, reject) => {
          resolve('Resolved 1');
        })
        .then(res => console.log(res))
        .catch(err => console.log(err));    
      };
    
      promise2 = () => {
        return new Promise((resolve, reject) => {
          resolve('Resolved 2');
        })
        .then(res => {
          console.log(res);
          this.promise3();
        })
        .catch(err => console.log(err));    
      };
    
      promise3 = () => {
        new Promise((resolve, reject) => {
          resolve('Resolved 3');
        })
        .then(res => console.log(res))
        .catch(err => console.log(err));    
      };
    
      start = () => {
        console.log('The Start');
    
        this.timeout();
        this.promise1();
        this.promise2();
    
        console.log('The End');
      };
    };
    
    여기서 우리는 간단한 약속과 유사한 것들을 보았다.가장 큰 차이점은 연쇄 승낙 2와 3이다.여기서 우리는 간단한 약속 예시와 같은 내용을 보아야 한다. 모든 약속은 시간을 초과하여 운행하기 전에 완성된다.

    발전기 절류


    합작 합병과 선점 합병.
  • 상태기의 문법 형식.
  • 추리 문제 해결에 관하여.
  • 비실행을 완료 행위로 허용합니다.국부 저지만 가능합니다.
  • 생성기가 교체기를 되돌려줍니다.
  • export function * throttle(func, time) {
      let timerID = null;
      function throttled(arg) {
        clearTimeout(timerID);
        timerID = setTimeout(func.bind(window, arg), time);
      }
      while(true) throttled(yield);
    }
    
    export class GeneratorThrottle {
    
      constructor() {};
    
      start = () => {
        thr = throttle(console.log, 3000);
        thr.next('');
      };
    };
    
    여기, 발전기가 작동할 때,thr는 운행 컨트롤러로 초기화됩니다.3초 후에 기록하다.

    현재, 우리는 초기화된 후, 다음 함수가 세 번 호출된 것을 볼 수 있다...그러나 컨트롤러는 3초 동안 창이 끝날 때 터치됩니다.

    사용자 상호 작용


    export class UserInteraction {
    
      constructor() {};
    
      dragStart = (event) => {
        event.dataTransfer.setData('text/plain', event.target.id);
        console.log('drag start', event);
      };
    
      dragOver = (event) => {
        event.preventDefault();
        event.dataTransfer.dropEffect = 'move';
        console.log({ x: event.pageX, y: event.pageY });
      };
    
      drop = (event) => {
        const id = event.dataTransfer.getData('text');
        console.log('drop', id);
        const element = document.getElementById('drag');
        event.target.appendChild(element);
      };
    }
    
    기본적으로 이 코드는 드래그 앤 드롭 이벤트가 JavaScript에 대해 스레드를 잠그지 않는 것을 볼 수 있도록 합니다.

    이벤트 탐지기


    이벤트 탐지기는 동기화됩니다 (비동기식)
    export class EventListeners {
      _btn = null;
      _time = 100;
    
      constructor() {};
    
      output = (content) => {
        console.log(content);
      };
    
      setupListeners = () => {
        this._btn.addEventListener('click', this.output.bind(null, 'Click Handler 1'));
        this._btn.addEventListener('click', this.output.bind(null,'Click Handler 2'));
      };
    
      triggerListeners = () => {
        setTimeout(() => {
          console.log('The Start');
          this._btn.click();
          console.log('The End');
        }, this._time);
      };
    
      start = () => {
        this._btn = document.getElementById('event-listener-link');
        this.setupListeners();
        this.triggerListeners();
      };
    }
    
    우리는 클릭 사건이 모두 촉발되는 것을 보아야 한다. 왜냐하면...

    네트워크 종사자


    노동자 수
  • 숫자는 브라우저에 따라 다릅니다.최적치는 20 안팎인 것 같다.참조(StackOverflow on Number of Web Workers Limit)[https://stackoverflow.com/questions/13574158/number-of-web-workers-limit].
  • 이것은 관건적인 숫자다.js, 인터넷 종사자로...
    onmessage = function() {
      for (let step = 0, len = 10; step <= len; step++) {
        postMessage(step * 10);
        const start = Date.now();
        while (Date.now() < start + 1000) {};
      }  
    }
    
    이것은 웹 워커 코드를 사용하는 코드입니다...
    export class WebWorkers {
      _worker = new Worker('scripts/presentation/crunch-numbers.js');
      _inlineProgress = null;
      _workerProgress = null;
    
      contructor() {};
    
      crunchNumbersInline = (callback) => {
        for (let step = 0, len = 10; step <= len; step++) {
          callback(step * 10);
          const start = Date.now();
          while (Date.now() < start + 1000) {};
        }
      };
    
      displayPercentInline = (percent) => {
        console.log(`inline percent: ${percent}`);
        this._inlineProgress.value = percent;
      };
    
      displayPercent = (message) => {
        console.log(`web-worker percent: ${message.data}`);
        this._workerProgress.value = message.data;
      }
    
      runSync = () => {
        this._inlineProgress = document.getElementById('inline-worker');
        this.crunchNumbersInline(this.displayPercentInline);
      };
    
      runAsync = () => {
        this._workerProgress = document.getElementById('web-worker');
        this._worker.postMessage('start');
        this._worker.onmessage = this.displayPercent;
      };
    
      start = (async = false) => {
        if (!async) {
          this.runSync();
        } else {
          this.runAsync();
        }
      };
    }
    
    관련 HTML 페이지를 실행하지 않으면 여기에서 무슨 일이 일어났는지 보기 어렵다.이것은 내연 프로세스가 스레드에 잠겨 있음을 나타냅니다. 백분율은 시간이 만료되기 전에 아무것도 하지 않고 한 번의'점프'에서 100% 를 표시합니다.
    웹 Worker의 경우 10% 씩 증가분이 정확하게 표시되고 JavaScript는 스레드에 잠기지 않습니다.

    부하 시간


    원본 내용 (코드를 통해 변경하고자 함)
    올바른 내용 변경 (코드를 통해)
    위에 표시된 것은...
    class LoadTiming {
      _time = 10000;
    
      constructor() {};
    
      loadSync = () => {
        const element = document.getElementById('first-timing');
        if (element) {
          element.innerHTML = 'Changed Content Correctly (via code)';
        }
      };
    
      loadAsync = () => {
        setTimeout(() => {
          const element = document.getElementById('second-timing');
          if (element) {
            element.innerHTML = 'Changed Content Correctly (via code)';
          }
        }, this._time);
      };
    
      start = () => {
        this.loadSync();
        this.loadAsync();
      };
    }
    
    const code11 = new LoadTiming();
    code11.start();
    
    보시다시피 위의 코드는 즉시 동기화와 비동기화 코드를 불러옵니다.여기에 있는 JavaScript는 헤더 내용에 불러오기 때문에, 주체 내용(DOM)이 위치하기 전에 실행되며, 동기화 기능은 getelementById에서 소리 없이 실패합니다.비동기식 버전은 DOM이 준비되었는지 확인하고 코드의 내용을 업데이트할 수 있는 충분한 지연이 있습니다.

    시간 초과 타이머 설정


    이 코드에서 set Timeout 지연이 실제로 얼마나 되는지 보고 싶습니다.
    시간 초과 지연 설정은 얼마나 됩니까?

  • (테스트 지연 조심... 1000회 교체)
  • export class SetTimeoutTimer {
      _repetitions = 0;
      _totalRepetitions = 1000;
      _delay = 0;
    
      _totalActualDelay = 0;
    
      constructor() {};
    
      getActualDelay = () => {
        return this._totalActualDelay / this._totalRepetitions;
      };
    
      iterate = () => {
        let start = new Date();
        setTimeout(() => {
          this._totalActualDelay += new Date() - start;
          this.testDelay();
        }, this._delay);
      };
    
      testDelay = () => {
        if (this._repetitions++ > this._totalRepetitions) {
          console.log(`Requested Delay: ${this._delay}, Acual Average Delay: ${this.getActualDelay()}`);
          return;
        }
        this.iterate();
      };
    
      start = (delay = 0) => {
        this._delay = delay;
        this._repetitions = 0;
        this._totalActualDelay = 0;
        this.testDelay();
      };
    }
    
    여기 답은 42가 아니에요.setTimeout의 기본값은 보통 4ms입니다.나는 서로 다른 기계와 브라우저에서 4ms에서 8ms 정도의 변화를 보았다.또한 여기에서 보듯이 이것은 사실상 정수가 아닙니다. (4ms에서 촉발되지 않으며, 그 후 일정 시간 동안 자바스크립트에서 처리할 수 있습니다.)

    ES2017 비동기식/대기

  • 약속된 사용을 확장했다.
  • 보기와 느낌이 동기화된 비동기 코드를 작성한다.
  • 문법을 정리하여 더욱 읽을 수 있도록 했다.
  • export class AsyncAwait {
      _time = 2000;
      _resolve = true;
      _success = `Doing something here ... after ${this._time}ms.`;
      _fail = `Failed here ... after ${this._time}ms.`;
    
      constructor() {};
    
      asyncProcess = () => {
        return new Promise((resolve, reject) => {
          setTimeout(() => { (this._resolve === true) ? resolve(this._success) : reject(this._fail); }, this._time);
        });
      };
    
      asyncAwait = async () => {
        try {
          console.log(await this.asyncProcess());
        } catch (error) {
          console.log(error);
        }
      };
    
      start = (resolveState = true) => {
        this._resolve = resolveState;
        console.log('The Start');
        this.asyncAwait();
        console.log('The End');
      };
    }
    
    기본적으로, 이 코드가 시작될 때,promise의 비동기/대기 버전을 실행합니다.실제로 강연에서, 나는 약속을 거부하는 것을 어떻게 처리하는지 물었고, 나는 그것을 찾아야 했다. (try/catch block)
    다음은 정확한 해석을 위한 비동기/대기...

    ... 거부와 같은 코드...

    총결산


    우리는 검사했다.
  • 리셋된 동기화 및 비동기 코드 사용...디버그
  • ES2015 약속 체인.
  • 발전기(절류)
  • 사용자 상호 작용.
  • 이벤트 탐지기(동기화).
  • 네트워크 종사자.
  • 부하 정시.
  • ES2017 비동기식/대기 중
  • 결론


    이 모든 것은 JavaScript의 동기화와 비동기화 행위에 대한 저의 몇 차례의 강연에서 나온 것입니다.실제 통화는 일종의 반실시간 인코딩 통화다.강연이 끝난 후 실제 코드를 사람들에게 남겨 두는 것이 긴장되어 이 글을 만들었다.
    Single-Threaded and Asynchronous JavaScript?

    좋은 웹페이지 즐겨찾기