비동기식은 CPU 점유율을 높이고 항상 바쁘게 유지하는 것입니다.
일부 작업(가장 일반적인 작업은 I/O)에는 CPU 참여가 필요하지 않으며 시간이 많이 걸립니다. 비동기식을 사용하지 않으면 차단 상태가 형성되어 CPU가 유휴 상태가 되고 페이지가 정지됩니다.
비동기 환경에서 I/O 작업이 발생하면 CPU는 I/O 작업을 옆으로 미루고(이때 I/O는 다른 컨트롤러에 의해 인계되고 데이터는 여전히 전송 중임) 다음 작업을 처리합니다. , I/O 작업이 완료되기를 기다립니다. CPU(콜백은 알림 방법임)가 다시 작동하도록 알립니다.
"JavaScript 비동기 및 콜백"의 핵심 내용은 비동기 작업의 구체적인 종료 시간이 불확실하다는 것입니다. 비동기 작업이 완료된 후 후속 처리를 정확하게 수행하려면 콜백을 비동기 함수에 전달해야 합니다. 작업을 완료하고 다음 작업을 계속하세요.
콜백은 비동기적으로 구현하기가 매우 간단할 수 있지만 여러 중첩으로 인해 콜백 지옥을 형성할 수 있습니다. 콜백 지옥을 피하려면 중첩 프로그래밍을 선형 프로그래밍으로 정의하고 변경해야 합니다.
Promise
는 JavaScript
에서 콜백 지옥을 처리하는 최고의 솔루션입니다.
Promise
는 "promise"로 번역할 수 있습니다. 비동기 작업을 캡슐화하여 Promise
라고 부를 수 있습니다. 즉, 비동기 작업이 끝난 후 명확한 신호를 제공하겠다고 약속하고 약속합니다!
Promise
:
let promise = new Promise(function(resolve,reject){ // 비동기 작업})
위 구문을 통해 비동기 작업을 Promise
로 캡슐화할 수 있습니다. Promise
생성할 때 전달되는 함수는 executor
자라고도 알려진 비동기 작업을 처리하는 방법입니다.
resolve
및 reject
JavaScript
자체에서 제공하는 콜백 함수입니다. executor
작업을 완료할 때 호출할 수 있습니다.
resolve(result)
- 성공적으로 완료되면 result
반환됩니다.reject(error)
- error
이 실패하면 error
가 생성됩니다.executor
Promise
생성된 직후 자동으로 실행되며 실행 상태는 Promise
의 내부 속성 상태를 변경합니다.
state
- 초기 pending
, resolve
호출된 후 fulfilled
으로 변환되거나 rejected
됨 reject
호출된 경우result
——처음에는 undefined
으며, 이후에는 resolve(value)
이 호출된 후 value
되거나 reject
호출된 후 error
됩니다.비동기 함수 fs.readFile
. . executor
에 전달할 수 있습니다. 파일 읽기 작업은 파일에서 수행되므로 비동기 작업이 캡슐화됩니다.
다음 코드는 fs.readFile
함수를 캡슐화하고, 성공적인 결과를 처리하기 위해 resolve(data)
을 사용하고, 실패한 결과를 처리하기 위해 reject(err)
사용합니다.
코드는 다음과 같습니다:
let promise = new Promise((resolve, Reject) => { fs.readFile('1.txt', (err, data) => { console.log('1.txt 읽기') if (err) 거부 (err) 해결(데이터) })})
이 코드를 실행하면 "Read 1.txt"라는 단어가 출력되어 Promise
생성된 직후 파일 읽기 작업이 수행됨을 증명합니다.
Promise
일반적으로 비동기 코드를 내부적으로 캡슐화하지만 비동기 코드만 캡슐화하는 것은 아닙니다.
위의 Promise
사례는 파일 생성이 완료된 후 즉시 파일을 읽는 작업을 요약합니다. Promise
실행 결과를 얻으려면 then
, catch
및 finally
세 가지 방법을 사용해야 합니다.
Promise
의 then
메소드는 Promise
실행이 완료된 후 작업을 처리하는 데 사용할 수 있습니다. 구문은 다음과 같습니다.
promise.then(function(result),function(error))
result
는 resolve
받은 값입니다.error
는 reject
받은 매개변수입니다
. promise = new Promise((해결, 거부) => { fs.readFile('1.txt', (err, data) => { console.log('1.txt 읽기') if (err) 거부 (err) 해결(데이터) })})약속합니다.그러면( (데이터) => { console.log('성공적으로 실행되었습니다. 결과는 다음과 같습니다.' + data.toString()) }, (오류) => { console.log('실행 실패, 오류:' + err.message) })
파일 읽기가 성공적으로 실행되면 첫 번째 함수가 호출됩니다:
PS E:CodeNodedemos 3-callback> node .index.js 1.txt 읽기 성공적으로 실행되면 결과는 1
delete 1.txt
입니다. 실행이 실패하면 두 번째 함수가 호출됩니다:
PS E:CodeNodedemos 3-callback> node .index.js 1.txt 읽기 ENOENT: no such file ordirectory, open 'E:CodeNodedemos 3-callback1.txt' 오류로 인해 실행이 실패했습니다.
성공적인 실행 결과에만 초점을 맞추면 하나만 전달할 수 있습니다. 콜백 함수:
promise .then((data)=>{ console.log('성공적으로 실행되었습니다. 결과는' + data.toString())})
이 시점에서 파일의 비동기 읽기 작업을 구현했습니다.
실패 결과에만 초점을 맞추면 첫 then
콜백에 null
전달할 수 있습니다: promise.then(null,(err)=>{...})
.
아니면 좀 더 우아한 방법을 사용하세요: promise.catch((err)=>{...})
let promise = new Promise((resolve, Reject) => { fs.readFile('1.txt', (err, data) => { console.log('1.txt 읽기') if (err) 거부 (err) 해결(데이터) })})promise.catch((err)=>{ console.log(err.message)})
.catch((err)=>{...})
및 then(null,(err)=>{...})
정확히 동일한 효과를 갖습니다.
.finally
는 promise
의 결과와 상관없이 실행되는 함수입니다. try...catch...
구문의 finally
와 동일한 목적을 가지며, 결과와 관련 없는 작업을 처리할 수 있습니다.
예:
new Promise((resolve,reject)=>{ //뭔가...}).finally(()=>{console.log('결과에 관계없이 실행')}).then(result=>{...}, err=>{...} )finally 콜백은 매개변수
fs.readFile()
메서드는 10개의 파일을 순차적으로 읽어 내용을 출력합니다. 10개의 파일을 순차적으로
fs.readFile()
자체는 비동기식이므로 콜백 중첩을 사용해야 합니다. 코드는 다음과 같습니다.
fs.readFile('1.txt', (err, data) => { console.log(data.toString()) //1 fs.readFile('2.txt', (err, data) => { console.log(data.toString()) fs.readFile('3.txt', (err, data) => { console.log(data.toString()) fs.readFile('4.txt', (err, 데이터) => { console.log(data.toString()) fs.readFile('5.txt', (err, 데이터) => { console.log(data.toString()) fs.readFile('6.txt', (err, data) => { console.log(data.toString()) fs.readFile('7.txt', (err, data) => { console.log(data.toString()) fs.readFile('8.txt', (err, data) => { console.log(data.toString()) fs.readFile('9.txt', (err, 데이터) => { console.log(data.toString()) fs.readFile('10.txt', (err, 데이터) => { console.log(data.toString()) // ==> 지옥의 문}) }) }) }) }) }) }) }) })})
위의 코드로 작업을 완료할 수 있지만 호출 중첩이 증가할수록 코드 수준이 더 깊어지고 유지 관리의 어려움이 증가합니다. 특히 루프와 조건문이 많이 포함될 수 있는 실제 코드를 사용할 경우 더욱 그렇습니다. 예제에서는 간단한 console.log(...)
.
콜백을 사용하지 않고 다음 코드에 따라 fs.readFile()
순서대로 직접 호출하면 어떻게 될까요?
//참고: 이는 fs.readFile('1.txt', (err, data) => {를 작성하는 잘못된 방법입니다. console.log(data.toString())})fs.readFile('2.txt', (err, data) => { console.log(data.toString())})fs.readFile('3.txt', (err, data) => { console.log(data.toString())})fs.readFile('4.txt', (err, data) => { console.log(data.toString())})fs.readFile('5.txt', (err, data) => { console.log(data.toString())})fs.readFile('6.txt', (err, data) => { console.log(data.toString())})fs.readFile('7.txt', (err, data) => { console.log(data.toString())})fs.readFile('8.txt', (err, data) => { console.log(data.toString())})fs.readFile('9.txt', (err, data) => { console.log(data.toString())})fs.readFile('10.txt', (err, data) => { console.log(data.toString())})
다음은 테스트 결과입니다(실행마다 결과가 다름).
PS E:CodeNodedemos 3-callback> node .index. js12346957108이
이러한 비순차적 결과를 생성하는 이유는 다중 스레드 병렬 처리가 아니라 비동기식이기 때문입니다 . 비동기는 단일 스레드에서 달성될 수 있습니다.
여기서 이 오류 사례를 사용하는 이유는 비동기성의 개념을 강조하기 위한 것입니다. 이 결과가 발생하는 이유를 이해하지 못한다면 돌아가서 수업을 보충해야 합니다!
Promise
사용하여 비동기식 순차 파일 읽기 문제를 해결하는 아이디어:
promise1
읽도록 파일을 캡슐화하고, 결과를 반환하기 resolve
promise1.then
사용하여 파일 읽기 결과를 수신하고 출력합니다promise2
promise1.then
promise1.then
promise2.then
호출promise2.then
에 새로운 promise3
객체를 생성하고,promise3.then
호출하여 판독 결과를 수신하고 출력합니다코드는 다음과 같습니다:
let promise1 = new Promise( (해결, 거부) => { fs.readFile('1.txt', (err, data) => { if (err) 거부 (err) 해결(데이터) })}) promise2 = promise1.then( 데이터 => { console.log(data.toString()) return new Promise((해결, 거부) => { fs.readFile('2.txt', (err, data) => { if (err) 거부 (err) 해결(데이터) }) }) })promise3 = promise2.then( 데이터 => { console.log(data.toString()) return new Promise((해결, 거부) => { fs.readFile('3.txt', (err, data) => { if (err) 거부 (err) 해결(데이터) }) }) })promise4 = promise3.then( 데이터 => { console.log(data.toString()) //..... })... ...
이런 식으로 원래 중첩된 콜백 지옥을 선형 모드로 작성합니다.
하지만 코드에는 여전히 문제가 있습니다. 관리 측면에서는 코드가 더욱 아름다워졌지만 코드 길이가 크게 늘어납니다.
위의 코드는 너무 길다: 반복되는 함수로 코드를 캡슐화하고, 파일 읽기 및 출력 작업을 완료하고, 중간 Promise의 변수 생성을 생략하고, .then을 연결하는 두 단계를 통해 코드 양을 줄일
promise
.then
다음과 같은 코드:
function myReadFile (path) { return new Promise((해결, 거부) => { fs.readFile(경로, (err, 데이터) => { if (err) 거부 (err) console.log(data.toString()) 해결하다() }) })}myReadFile('1.txt') .then(data => { return myReadFile('2.txt') }) .then(data => { return myReadFile('3.txt') }) .then(data => { return myReadFile('4.txt') }) .then(data => { return myReadFile('5.txt') }) .then(data => { return myReadFile('6.txt') }) .then(data => { return myReadFile('7.txt') }) .then(data => { return myReadFile('8.txt') }) .then(data => { return myReadFile('9.txt') }) .then(data => { return myReadFile('10.txt') })
myReadFile
메서드는 새로운 Promise
반환하므로 .then
메서드를 직접 실행할 수 있습니다. 이 프로그래밍 방법을 체인 프로그래밍 이라고 합니다.
코드 실행 결과는 다음과 같습니다.
PS E:CodeNodedemos 3-callback> node .index.js12345678910
이로써 비동기 및 순차 파일 읽기 작업이 완료됩니다.
참고: 각 단계의
.then
메소드에서 새Promise
객체를 반환해야 합니다. 그렇지 않으면 이전Promise
수신됩니다.이는 각
then
메서드가 계속해서Promise
를 아래쪽으로 전달하기 때문입니다.