
비동기식은 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 읽도록 파일을 캡슐화하고, 결과를 반환하기 resolvepromise1.then 사용하여 파일 읽기 결과를 수신하고 출력합니다promise2promise1.then
promise1.thenpromise2.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를 아래쪽으로 전달하기 때문입니다.