이 기사는 실제 개발 및 학습에서 nodejs에 대한 개인적인 이해를 바탕으로 나중에 참고할 수 있도록 작성되었습니다.
I/O: 입력/출력, 시스템의 입력 및 출력입니다.
시스템은 사람과 같은 개인으로 이해될 수 있습니다. 말하면 출력이고, 들으면 입력입니다.
Blocking I/O와 Non-Blocking I/O의 차이점은 입력에서 출력까지의 기간 동안 시스템이 다른 입력을 받을 수 있는지 여부에 있습니다. 다음은 차단 I/O와 비차단 I/O가 무엇인지 보여주는 두 가지 예입니다.
1. 식사 만들기
우선 시스템의 범위를 결정해야 합니다. 이 예에서는 구내식당 아줌마가 식당의 웨이터를 입력으로 주문하고 출력으로 음식을 제공하는 시스템으로 생각하세요.
그러면 음식을 주문하고 서빙하는 사이에 다른 사람의 주문을 받아들일 수 있는지, 차단 I/O인지 비차단 I/O인지 판단할 수 있습니다. 식당 이모님은 주문 시 다른 학생들을 위해 주문할 수 없습니다. 학생이 주문을 마치고 음식을 대접한 후에야 다음 학생의 주문을 받아들일 수 있기 때문에 식당 이모님이 I/O를 차단하고 있습니다.
레스토랑 웨이터의 경우 주문 후, 손님이 요리를 제공하기 전에 다음 손님에게 서비스를 제공할 수 있으므로 웨이터는 비차단 I/O를 갖습니다.
2. 집안일을 하세요빨래를 할 때 세탁기를 기다릴 필요 없이 이때 책상, 옷 등을 정리한 후 바닥을 쓸 수 있습니다. 이때 빨래를 널면 총 25분 정도 소요됩니다.
세탁은 실제로 논블로킹 I/O입니다. 세탁기에 옷을 넣고 세탁을 마치는 사이에도 다른 일을 할 수 있습니다.
Non-blocking I/O가 성능을 향상시킬 수 있는 이유는 불필요한 대기 시간을 줄일 수 있기 때문입니다.비차단 I/O를 이해하는 열쇠는 :
I/O에 대한 시스템 경계를 결정
합니다. 이는 매우 중요합니다. 위의 레스토랑 예시처럼 시스템이 레스토랑 전체로 확장되면 셰프는 확실히 블로킹 I/O가 될 것입니다.입니다. 아래 아키텍처 다이어그램을 스레드 유지 관리에 따라 구분하면 왼쪽 점선은 nodejs 스레드이고 오른쪽 점선은 C++ 스레드입니다.
이제 nodejs 스레드는 데이터베이스를 쿼리해야 합니다. 이는 일반적인 I/O 작업이며 I/O 결과를 기다리지 않고 계속해서 많은 양의 작업을 처리합니다. 다른 작업에 대한 컴퓨팅 성능을 계산합니다.결과가 나올 때까지 기다렸다가 nodejs 스레드로 반환합니다. 결과를 얻기 전에 nodejs 스레드는 다른 I/O 작업도 수행할 수 있으므로 비차단입니다.
nodejs 스레드는 왼쪽 부분이 웨이터이고 C++ 스레드가 요리사인 것과 동일합니다.
그래서 노드의 비차단 I/O는 C++ 작업자 스레드를 호출하여 완료됩니다.
C++ 스레드가 결과를 얻었을 때 nodejs 스레드에 알리는 방법은 무엇입니까? 대답은 이벤트 중심입니다.
Event-driven
Non-blocking: I/O 중에 함수가 즉시 반환됩니다. /O, 프로세스는 I/O 완료를 기다리지 않습니다.
그런 다음 반환된 결과를 아는 방법은 이벤트 기반을 사용해야 합니다.
소위 이벤트 중심은 프런트엔드 클릭 이벤트와 동일하게 이해하면 됩니다. 먼저 클릭 이벤트를 작성하지만 언제 트리거될지는 알 수 없습니다. 메인 스레드는 이벤트 기반 기능을 실행합니다.
이 모드도 관찰자 모드입니다. 즉, 먼저 이벤트를 듣고 트리거되면 실행합니다. 그렇다면 이벤트 드라이브를 구현하는 방법은 무엇일까요? 대답은
비동기 프로그래밍입니다.
비동기 프로그래밍
glob(__dirname+'/**/*', (err, res) => { result = res console.log('get result') })
nodejs 콜백 함수의 첫 번째 매개변수는 오류이고 그 다음 매개변수는 결과입니다. 왜 이런 일을 하는가?
try { interview(function () { console.log('smile') }) } catch(err) { console.log('cry', err) } function interview(callback) { setTimeout(() => { if(Math.random() <p>실행 후 잡히지 않고 오류가 전역적으로 발생하여 전체 nodejs 프로그램이 중단되었습니다. </p><p><img src="https://img.php.cn/upload/image/244/886/980/1657110712466688.png" title="1657110712466688.png" alt="nodejs의 여러 핵심 노드를 이해하기 위한 요약 및 공유"></p><p>은 이벤트 루프가 열릴 때마다 setTimeout이 이벤트 루프를 다시 열기 때문에 try catch로 캡처되지 않습니다. try catch는 이전 이벤트 루프의 호출 스택에 속하며 콜백 함수입니다. setTimeout이 실행되면 호출 스택이 다릅니다. 이 새 호출 스택에는 try catch가 없으므로 오류가 전역적으로 발생하고 포착될 수 없습니다. 자세한 내용은 이 기사<a href="https://juejin.cn/post/6995749646366670855" target="_blank" title="https://juejin.cn/post/6995749646366670855">try catch에 비동기 대기열을 사용할 때의 문제</a>를 참조하세요. </p><p>그럼 우리는 어떻게 해야 할까요? 오류를 매개변수로 사용: </p><pre class="brush:php;toolbar:false">function interview(callback) { setTimeout(() => { if(Math.random() <p> 하지만 이는 더 번거롭고 콜백에서 판단해야 하므로 성숙한 규칙이 생성됩니다. 첫 번째 매개변수가 존재하지 않으면 실행이 실패한다는 의미입니다. 성공적인. </p><pre class="brush:php;toolbar:false">function interview(callback) { setTimeout(() => { if(Math.random() <h3 data-id="heading-5"><strong>비동기 프로세스 제어</strong></h3><p>nodejs의 콜백 작성 방식은 콜백 영역을 가져올 뿐만 아니라 <strong>비동기 프로세스 제어</strong>문제도 가져옵니다. </p><p>비동기 프로세스 제어는 주로 동시성이 발생할 때 동시성 논리를 처리하는 방법을 말합니다. 위의 예를 사용하면 동료가 두 회사를 인터뷰하는 경우 두 회사를 성공적으로 인터뷰할 때까지 세 번째 회사의 인터뷰를 받지 않습니다. 그렇다면 이 논리를 어떻게 작성해야 할까요? 전역적으로 count:</p><pre class="brush:php;toolbar:false">var count = 0 interview((err) => { if (err) { return } count++ if (count >= 2) { // 处理逻辑 } }) interview((err) => { if (err) { return } count++ if (count >= 2) { // 处理逻辑 } })
변수를 추가해야 합니다. 위와 같이 작성하는 것은 매우 번거롭고 보기 흉합니다. 따라서 promise와 async/await의 작성 방법은 나중에 등장했습니다.
현재 이벤트 루프에서는 결과를 얻을 수 없지만 미래 이벤트 루프에서는 결과를 얻을 수 있습니다. 멍청이가 말하는 것과 매우 유사합니다.
약속은 쓰레기일 뿐만 아니라 상태 기계이기도 합니다.
const pro = new Promise((resolve, reject) => { setTimeout(() => { resolve('2') }, 200) }) console.log(pro) // 打印:Promise { <pending> }</pending>
then 또는 catch를 실행하면 새 Promise가 반환됩니다. Promise의 최종 상태는 then 및 catch의 콜백 함수 실행 결과에 따라 결정됩니다.
function interview() { return new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() > 0.5) { resolve('success') } else { reject(new Error('fail')) } }) }) } var promise = interview() var promise1 = promise.then(() => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('accept') }, 400) }) })
promise1의 상태는 반환된 promise의 상태, 즉 반환된 promise가 실행된 후 promise1의 상태에 따라 결정됩니다. 이것의 이점은 무엇입니까? 이렇게 하면 콜백 지옥 문제를 해결할 수 있습니다.
var promise = interview() .then(() => { return interview() }) .then(() => { return interview() }) .then(() => { return interview() }) .catch(e => { console.log(e) })
그런 다음 반환된 Promise의 상태가 거부되면 첫 번째 catch가 호출되고 후속 then은 호출되지 않습니다. 기억하세요: 거부된 통화는 첫 번째 캐치이고 해결된 통화는 그 다음 첫 번째입니다.
promise가 단지 지옥 콜백을 해결하는 것이라면 promise의 주요 기능은 비동기 프로세스 제어 문제를 해결하는 것입니다. 동시에 두 회사를 인터뷰하고 싶다면:
function interview() { return new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() > 0.5) { resolve('success') } else { reject(new Error('fail')) } }) }) } promise .all([interview(), interview()]) .then(() => { console.log('smile') }) // 如果有一家公司rejected,就catch .catch(() => { console.log('cry') })
sync/await 정확히 무엇입니까:
console.log(async function() { return 4 }) console.log(function() { return new Promise((resolve, reject) => { resolve(4) }) })
인쇄된 결과는 동일합니다. 즉, async/await는 단지 구문상의 설탕일 뿐입니다. 약속을 위해.
우리는 try catch가 오류를 호출 스택에 따라 캡처하며 호출 스택 위의 오류만 캡처할 수 있다는 것을 알고 있습니다. 하지만 Wait를 사용하면 호출 스택의 모든 함수에서 오류를 잡을 수 있습니다. setTimeout과 같은 다른 이벤트 루프의 호출 스택에서 오류가 발생하더라도 마찬가지입니다.
인터뷰 코드를 변환하고 나면 코드가 훨씬 간소화된 것을 확인할 수 있습니다.
try { await interview(1) await interview(2) await interview(2) } catch(e => { console.log(e) })
병렬 작업이라면 어떨까요?
await Promise.all([interview(1), interview(2)])
nodejs의 비차단 I/0으로 인해 I/O 결과를 얻으려면 이벤트 중심 접근 방식을 사용해야 합니다. 비동기 프로그래밍은 다음과 같습니다. 콜백 함수와 같이 사용됩니다. 그렇다면 결과를 얻기 위해 이러한 콜백 함수를 실행하는 방법은 무엇입니까? 그런 다음 이벤트 루프를 사용해야 합니다.
이벤트 루프는 nodejs의 비차단 I/O 기능을 구현하는 핵심 기반입니다. 비차단 I/O와 이벤트 루프는 모두 libuv
이 C++ 라이브러리에서 제공하는 기능입니다.
代码演示:
const eventloop = { queue: [], loop() { while(this.queue.length) { const callback = this.queue.shift() callback() } setTimeout(this.loop.bind(this), 50) }, add(callback) { this.queue.push(callback) } } eventloop.loop() setTimeout(() => { eventloop.add(() => { console.log('1') }) }, 500) setTimeout(() => { eventloop.add(() => { console.log('2') }) }, 800)
setTimeout(this.loop.bind(this), 50)
保证了50ms就会去看队列中是否有回调,如果有就去执行。这样就形成了一个事件循环。
当然实际的事件要复杂的多,队列也不止一个,比如有一个文件操作对列,一个时间对列。
const eventloop = { queue: [], fsQueue: [], timerQueue: [], loop() { while(this.queue.length) { const callback = this.queue.shift() callback() } this.fsQueue.forEach(callback => { if (done) { callback() } }) setTimeout(this.loop.bind(this), 50) }, add(callback) { this.queue.push(callback) } }
首先我们弄清楚了什么是非阻塞I/O,即遇到I/O立刻跳过执行后面的任务,不会等待I/O的结果。当I/O处理好了之后就会调用我们注册的事件处理函数,这就叫事件驱动。实现事件驱动就必须要用异步编程,异步编程是nodejs中最重要的环节,它从回调函数到promise,最后到async/await(使用同步的方法写异步逻辑)。
更多node相关知识,请访问:nodejs 教程!
위 내용은 nodejs의 여러 핵심 노드를 이해하기 위한 요약 및 공유의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!