이 글의 내용을 공부하기 전에 먼저 비동기의 개념을 이해해야 합니다. 먼저 강조할 점은 비동기와 병렬 사이에는 본질적인 차이가 있다는 것입니다.
병렬성은 일반적으로 병렬 컴퓨팅을 의미하며, 이는 여러 명령이 동시에 실행된다는 의미입니다. 이러한 명령은 동일한 CPU
의 여러 코어, 여러 CPU
, 여러 물리적 호스트 또는 여러 네트워크에서 실행될 수 있습니다.
동기화는 일반적으로 이전 작업이 완료되어야 다음 작업이 실행되는 미리 정해진 순서에 따라 작업을 실행하는 것을 의미합니다.
동기화에 해당하는 비동기식은 CPU
일시적으로 현재 작업을 따로 두고 다음 작업을 먼저 처리한 후 이전 작업의 콜백 알림을 받은 후 이전 작업으로 돌아가서 실행을 계속하는 것을 의미합니다. 두 번째 스레드에 참여합니다.
아마도 병렬성, 동기화성, 비동기성을 그림의 형태로 설명하는 것이 더 직관적일 것입니다. 처리해야 할 작업 A와 B가 두 개 있다고 가정해 보겠습니다. 병렬, 동기 및 비동기 처리 방법은 그림과 같은 실행 방법을 채택합니다. 다음 그림:
JavaScript
많은 비동기 함수를 제공하여 비동기 작업을 편리하게 실행할 수 있도록 해줍니다. 즉, 작업(함수) 실행을 지금 시작하지만 작업은 나중에 완료되며 특정 완료 시간이 됩니다. 확실하지 않습니다.
예를 들어 setTimeout
함수는 매우 일반적인 비동기 함수입니다. 또한 fs.readFile
및 fs.writeFile
도 비동기 함수입니다.
파일 복사 함수 copyFile(from,to)
사용자 정의와 같이 비동기 작업 사례를 직접 정의할 수 있습니다.
const fs = require('fs')function copyFile(from, to) { fs.readFile(from, (err, data) => { 만약 (오류) { console.log(err.message) 반품 } fs.writeFile(to, data, (err) => { 만약 (오류) { console.log(err.message) 반품 } console.log('복사 완료') }) })}
copyFile
함수는 먼저 의 매개변수 from
파일 데이터를 읽은 다음 매개변수가 to
파일에 데이터를 씁니다.
다음과 같이 copyFile
호출할 수 있습니다:
copyFile('./from.txt','./to.txt')//파일 복사
이때 copyFile(...)
뒤에 다른 코드가 있으면 프로그램은 작동하지 않습니다. wait copyFile
의 실행은 종료되지만 바로 아래로 실행됩니다. 프로그램은 파일 복사 작업이 언제 종료되는지 신경 쓰지 않습니다.
copyFile('./from.txt','./to.txt')//다음 코드는 위 코드의 실행이 끝날 때까지 기다리지 않습니다...
현시점에서는 모든 것이 정상인 것처럼 보이지만 만약 copyFile(...)
함수 이후에 ./to.txt
파일의 내용에 직접 액세스하면 어떻게 될까요?
다음과 같이 복사된 내용을 읽지 않습니다:
copyFile('./from.txt','./to.txt')fs.readFile('./to.txt',(err,data)= >{ ...})
프로그램을 실행하기 전에 ./to.txt
파일이 생성되지 않은 경우 다음 오류가 발생합니다:
PS E:CodeNodedemos 3-callback> node .index.js 완성된 복사 완료 PS E:CodeNodedemos 3-callback> 노드 .index.js 오류: ENOENT: 해당 파일 또는 디렉터리가 없습니다. 'E:CodeNodedemos 3-callbackto.txt'를 엽니다. 복사 완료
./to.txt
가 있어도 복사된 내용을 읽을 수 없습니다.
이 현상의 원인은 다음과 같습니다. copyFile(...)
비동기적으로 실행됩니다. 프로그램이 copyFile(...)
함수를 실행한 후 복사가 완료될 때까지 기다리지 않고 직접 아래쪽으로 실행하여 파일이 ./to.txt
가 존재하지 않는다는 오류 또는 파일 내용이 비어 있다는 오류(파일을 미리 생성한 경우)
콜백 함수 비동기 함수의 구체적인 실행 종료 시간을 결정할 수 없습니다. 예를 들어 readFile(from,to)
함수의 실행 종료 시간은 from
의 파일 크기에 따라 달라질 가능성이 높습니다.
그렇다면 문제는 어떻게 copyFile
실행의 끝을 정확하게 찾고 to
파일의 내용을 읽을 수 있느냐는 것입니다.
이를 위해서는 콜백 함수를 사용해야 합니다. 다음과 같이 copyFile
함수를 수정할 수 있습니다.
function copyFile(from, to, callback) { fs.readFile(from, (err, data) => { 만약 (오류) { console.log(err.message) 반품 } fs.writeFile(to, data, (err) => { 만약 (오류) { console.log(err.message) 반품 } console.log('복사 완료') callback()//복사 작업이 완료되면 콜백 함수가 호출됩니다.}) })}
이러한 방식으로 파일 복사가 완료된 후 즉시 일부 작업을 수행해야 하는 경우 이러한 작업을 콜백 함수에 작성할 수 있습니다.
function copyFile(from, to, callback) { fs.readFile(from, (err, data) => { 만약 (오류) { console.log(err.message) 반품 } fs.writeFile(to, data, (err) => { 만약 (오류) { console.log(err.message) 반품 } console.log('복사 완료') callback()//복사 작업이 완료되면 콜백 함수가 호출됩니다.}) })}copyFile('./from.txt', './to.txt', function () { //콜백 함수를 전달하고 "to.txt" 파일의 내용을 읽고 fs.readFile('./to.txt', (err, data) => { 만약 (오류) { console.log(err.message) 반품 } console.log(data.toString()) })})
./from.txt
파일을 준비했다면 위 코드를 직접 실행할 수 있습니다:
PS E:CodeNodedemos 3-callback> node .index.js 복사 완료 "Xianzong" 커뮤니티에 가입하고 저와 함께 불멸의 삶을 가꾸세요. 커뮤니티 주소: http://t.csdn.cn/EKf1h
이 프로그래밍 방법을 "콜백 기반" 비동기 프로그래밍 스타일이라고 합니다. 매개변수는 콜백을 제공해야 합니다. 작업이 끝난 후 호출하는 데 사용됩니다.
이 스타일은 JavaScript
프로그래밍에서 일반적입니다. 예를 들어 파일 읽기 함수 fs.readFile
및 fs.writeFile
은 모두 비동기 함수입니다.
콜백 함수는 비동기 작업이 완료된 후 후속 작업을 정확하게 처리할 수 있습니다. 여러 비동기 작업을 순차적으로 수행해야 하는 경우 콜백 함수를 중첩해야 합니다.
사례 시나리오:
파일 A와 파일 B를 순서대로 읽는 코드 구현:
fs.readFile('./A.txt', (err, data) => { 만약 (오류) { console.log(err.message) 반품 } console.log('파일 A 읽기: ' + data.toString()) fs.readFile('./B.txt', (err, data) => { 만약 (오류) { console.log(err.message) 반품 } console.log("파일 B 읽기: " + data.toString()) })})
실행 효과:
PS E:CodeNodedemos 3-callback> node .index.js 파일 A 읽기: Immortal Sect는 무한히 좋은데 누군가가 빠졌습니다. 파일 B 읽기: Immortal Sect에 가입하려면 http://t.csdn.cn/H1faI 링크가 있어야
읽을 수 있습니다
.A 다음에는 파일 B를 즉시 읽습니다.
파일 B 이후에 파일 C를 계속 읽으려면 어떻게 해야 합니까? 이를 위해서는 계속해서 콜백을 중첩해야 합니다.
fs.readFile('./A.txt', (err, data) => {//첫 번째 콜백 if (err) { console.log(err.message) 반품 } console.log('파일 A 읽기: ' + data.toString()) fs.readFile('./B.txt', (err, data) => {//두 번째 콜백 if (err) { console.log(err.message) 반품 } console.log("파일 B 읽기: " + data.toString()) fs.readFile('./C.txt',(err,data)=>{//세 번째 콜백... }) })})
즉, 여러 비동기 작업을 순차적으로 수행하려면 여러 수준의 중첩된 콜백이 필요합니다. 이는 수준 수가 적을 때 효과적이지만 중첩 시간이 너무 많으면 몇 가지 문제가 발생합니다. 질문이 발생합니다.
콜백 규칙
실제로 fs.readFile
의 콜백 함수 스타일은 예외가 아니지만 JavaScript
의 일반적인 규칙입니다. 우리는 앞으로 수많은 콜백 함수를 맞춤화할 예정이며, 이 규칙을 준수하고 좋은 코딩 습관을 형성해야 합니다.
관례는
callback
의 첫 번째 매개변수가 오류용으로 예약되어 있다는 것입니다. 오류가 발생하면 callback(err)
호출됩니다.callback(null, result1, result2,...)
호출됩니다.위의 규칙에 따라 콜백 함수에는 오류 처리와 결과 수신이라는 두 가지 기능이 있습니다. 예를 들어 fs.readFile('...',(err,data)=>{})
의 콜백 함수는 이 규칙을 따릅니다.
더 깊이 파고들지 않는다면 콜백을 기반으로 한 비동기식 메서드 처리가 이를 처리하는 데 아주 완벽한 방법인 것 같습니다. 문제는 비동기 동작이 연이어 발생하는 경우 코드가 다음과 같다는 것입니다.
fs.readFile('./a.txt',(err,data)=>{ 만약(오류){ console.log(err.message) 반품 } //결과 읽기 작업 fs.readFile('./b.txt',(err,data)=>{ 만약(오류){ console.log(err.message) 반품 } //결과 읽기 작업 fs.readFile('./c.txt',(err,data)=>{ 만약(오류){ console.log(err.message) 반품 } //결과 읽기 작업 fs.readFile('./d.txt',(err,data)=>{ 만약(오류){ console.log(err.message) 반품 } ... }) }) })})
위 코드의 실행 내용은 다음과 같습니다.
호출 횟수가 증가함에 따라 점점 더 많은 조건문을 포함하여 코드 중첩 수준이 점점 더 깊어지고, 결과적으로 오른쪽으로 계속 들여쓰기되는 혼란스러운 코드가 발생하여 읽기도 어렵고 읽기도 어렵습니다. 유지하다.
우리는 이러한 오른쪽(오른쪽 들여쓰기)으로의 지속적인 성장 현상을 " 콜백 지옥 " 또는 " 파멸의 피라미드 "라고 부릅니다!
fs.readFile('a.txt',(err,data)=>{ fs.readFile('b.txt',(err,data)=>{ fs.readFile('c.txt',(err,data)=>{ fs.readFile('d.txt',(err,data)=>{ fs.readFile('e.txt',(err,data)=>{ fs.readFile('f.txt',(err,data)=>{ fs.readFile('g.txt',(err,data)=>{ fs.readFile('h.txt',(err,data)=>{ ... /* 지옥으로 가는 문 ===> */ }) }) }) }) }) }) })})
위의 코드는 매우 규칙적으로 보이지만 예를 들어 이상적인 상황일 뿐입니다. 일반적으로 비즈니스 로직에는 많은 수의 조건문, 데이터 처리 작업 및 기타 코드가 있어 현재의 아름다운 질서를 방해하고 코드 변경이 어렵습니다.
다행스럽게도 JavaScript
다양한 솔루션을 제공하며 Promise
최고의 솔루션입니다.