이 기사의 내용을 연구하기 전에 먼저 비동기의 개념을 이해해야 합니다. 먼저 강조할 점은 비동기와 병렬 사이에는 본질적인 차이가 있다는 것입니다.
CPU
의 여러 코어, 여러 CPU
, 여러 물리적 호스트 또는 여러 네트워크에서 실행될 수 있습니다.CPU
일시적으로 현재 작업을 따로 두고 다음 작업을 먼저 처리한 후 이전 작업의 콜백 알림을 받은 후 이전 작업으로 돌아가서 실행을 계속하는 것을 의미합니다. 두 번째 스레드에 참여합니다.아마도 병렬성, 동기화성, 비동기성을 그림의 형태로 설명하는 것이 더 직관적일 것입니다. 처리해야 할 작업 A와 B가 두 개 있다고 가정해 보겠습니다. 병렬, 동기 및 비동기 처리 방법은 그림과 같은 실행 방법을 채택합니다. 다음 그림:
JavaScript
많은 비동기 함수를 제공하여 비동기 작업을 편리하게 실행할 수 있도록 해줍니다. 즉, 작업(함수) 실행을 지금 시작하지만 작업은 나중에 완료되며 특정 완료 시간이 됩니다. 확실하지 않습니다.
예를 들어 setTimeout
함수는 매우 일반적인 비동기 함수입니다. 또한 fs.readFile
및 fs.writeFile
도 비동기 함수입니다.
const fs = require('fs')
copyFile(from,to)
정의와 같이 비동기 작업 사례를 직접 정의할 수 있습니다.
함수 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') //다음 코드는 위 코드의 실행이 끝날 때까지 기다리지 않습니다...
./to.txt
까지는 모든 것이 정상인 것처럼 보이지만, copyFile(...)
함수 의 내용은 어떻게 되나요?
이것은 복사된 내용을 읽지 않습니다. 다음과 같이 하십시오:
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 읽기: 불멸의 종파는 무한히 좋지만 남자가 부족합니다.파일 B 읽기: 불멸의 종파에 가입하려면 링크가 있어야 합니다.
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
최고의 솔루션입니다.