nodejs의 비동기 프로그래밍 지식점 상세

소개


javascript는 기본적으로 단일 루트이기 때문에 코드가 새 루트를 만들어서 병행할 수 없다는 것을 의미합니다.그러나 브라우저에서 처음 실행된javascript에 대해 말하자면, 단일 루틴의 동기화 실행 환경은 페이지 클릭, 마우스 이동 등 응답 사용자의 기능을 만족시킬 수 없다.그래서 브라우저는 자바스크립트를 리셋하는 방식으로 페이지의 요청 이벤트에 비동기적으로 응답할 수 있는 API 그룹을 실현했다.
더 나아가 nodejs는 막히지 않는 I/O를 도입하여 비동기적인 개념을 파일 접근, 네트워크 호출 등으로 확장시켰다.
오늘 우리는 각종 비동기 프로그래밍의 장단점과 발전 추세를 깊이 있게 탐구할 것이다.

동기식 비동기식 및 비차단


nodejs의 비동기 프로그래밍을 토론하기 전에 비교적 헷갈리기 쉬운 개념을 토론합시다. 그것이 바로 동기화, 비동기, 막힘과 비막힘입니다.
이른바 막힘과 비막힘이란 프로세스나 루틴이 조작을 하거나 데이터를 읽고 쓸 때 기다려야 하는지, 기다리는 과정에서 다른 조작을 할 수 있는지를 가리킨다.
만약 기다림이 필요하고 기다림 과정에서 라인이나 프로세스가 다른 조작을 할 수 없으며 바보같이 기다릴 수밖에 없다면, 우리는 이 조작이 막혔다고 말할 것이다.
반대로 만약에 프로세스나 루틴이 조작을 하거나 데이터를 읽고 쓰는 과정에서 다른 조작을 할 수 있다면 우리는 이 조작이 막힌 것이 아니라고 말할 것이다.
동기화와 비동기화는 데이터에 접근하는 방식을 가리키며, 동기화는 주동적으로 데이터를 읽어야 하는 것을 가리키며, 이 읽기 과정은 막히거나 비틀어질 수 있다.비동기화란 주동적으로 데이터를 읽을 필요가 없고 수동적인 통지를 가리킨다.
분명히javascript의 리셋은 수동적인 알림입니다. 우리는 비동기 호출이라고 할 수 있습니다.

javascript의 리셋


javascript의 리셋은 비동기 프로그래밍의 매우 전형적인 예입니다.

document.getElementById('button').addEventListener('click', () => {
 console.log('button clicked!');
})
위의 코드에서 우리는 버튼에 클릭 이벤트 감청기를 추가했습니다. 만약에 클릭 이벤트를 감청하면 리셋 함수를 출발하여 해당하는 정보를 출력합니다.
리셋 함수는 일반적인 함수입니다.ddEventListener에 매개 변수로 전달되고 이벤트가 발생할 때만 호출됩니다.
지난 글에서 우리가 말한 set Timeout과 set Interval은 사실상 모두 비동기적인 리셋 함수이다.

리셋 함수 오류 처리


nodejs에서 리셋된 오류 정보를 어떻게 처리합니까?nodejs는 매우 교묘한 방법을 채택했다. nodejs에서 모든 리셋 함수 중의 첫 번째 파라미터가 오류 대상이므로 우리는 이 오류 대상의 존재 여부를 판단하여 해당하는 오류 처리를 할 수 있다.

fs.readFile('/ .json', (err, data) => {
 if (err !== null) {
 // 
 console.log(err)
 return
 }

 // , 。
 console.log(data)
})

지옥으로 돌아가다


javascript의 리셋은 매우 우수하지만 동기화 처리 문제를 효과적으로 해결했다.그러나 유감스럽게도 우리가 리셋 함수의 리셋 값에 의존하여 다음 조작을 해야 할 때 이 리셋 지옥에 빠질 것이다.
회조지옥이라고 하는 것은 좀 과장된 것이지만, 또한 한편으로는 회조 함수에 존재하는 문제점을 반영하였다.

fs.readFile('/a.json', (err, data) => {
 if (err !== null) {
 fs.readFile('/b.json',(err,data) =>{
  //callback inside callback
 })
 }
})
어떻게 해결하지?
ES6가 Promise를 도입하는 것을 두려워하지 마라. ES2017은 Async/Await를 도입하면 이 문제를 해결할 수 있다.

ES6의 Promise


Promise
Promise는 비동기 프로그래밍의 해결 방안으로 전통적인 해결 방안인'리셋 함수와 이벤트'보다 더욱 합리적이고 강력하다.
이른바 Promise란 간단하게 말하면 하나의 용기로 미래에 끝날 사건(일반적으로 비동기적인 조작)의 결과를 저장하고 있다.
문법적으로 프로미스는 비동기적인 조작에 대한 정보를 얻을 수 있는 대상이다.

Promise의 특징


Promise에는 다음과 같은 두 가지 특징이 있습니다.
1. 대상의 상태는 외부의 영향을 받지 않는다.
Promise 객체는 Pending(진행 중), Resolved(완료됨, Fulfilled) 및 Rejected(실패) 등 세 가지 상태를 나타내는 비동기식 작업입니다.
비동기적인 조작의 결과만 현재 어떤 상태인지 결정할 수 있고, 다른 조작도 이 상태를 바꿀 수 없다.
2. 일단 상태가 바뀌면 다시 변하지 않고 언제든지 이 결과를 얻을 수 있다.
Promise 객체의 상태 변경은 Pending에서 Resolved로, Pending에서 Rejected로 두 가지만 가능합니다.
이것은 사건(Event)과 완전히 다르다. 사건의 특징은 당신이 그것을 놓치면 감청하면 결과를 얻지 못한다는 것이다.

Promise의 이점

  • Promise는 비동기 조작을 동기화 조작의 절차로 표현하여 층층이 끼워 넣은 리셋 함수를 피한다..
  • Promise 객체는 통일된 인터페이스를 제공하여 비동기식 조작을 제어하는 것을 더욱 쉽게 합니다
  • Promise의 단점

  • Promise를 취소할 수 없습니다. 새로 만들면 바로 실행되고 중간에 취소할 수 없습니다
  • 리셋 함수를 설정하지 않으면 Promise 내부에서 던진 오류가 외부에 반영되지 않습니다..
  • Pending 상태에 있을 때 현재 어느 단계까지 진전되었는지 알 수 없다(막 시작했는지 곧 완성될 것이다)
  • Promise 사용법


    Promise 객체는 Promise 인스턴스를 생성하는 데 사용되는 구조 함수입니다.
    
    var promise = new Promise(function(resolve, reject) { 
    // ... some code 
    if (/*   */){ 
    resolve(value); 
    } else { reject(error); } 
    }
    );
    promise는 then 조작을 할 수 있고, then 조작은 두 개의 function 파라미터를 연결할 수 있다. 첫 번째 function 파라미터는 Promise를 구축할 때resolve의value이고, 두 번째 function 파라미터는 Promise의reject를 구축하는 error이다.
    
    promise.then(function(value) { 
    // success 
    }, function(error) { 
    // failure }
    );
    구체적인 예를 살펴보겠습니다.
    
    function timeout(ms){
     return new Promise(((resolve, reject) => {
      setTimeout(resolve,ms,'done');
     }))
    }
    
    timeout(100).then(value => console.log(value));
    Promise에서 setTimeout 방법을 호출하였으며, 정해진 시간에 Resolve 방법을 터치하여, 매개 변수done를 전송합니다.
    마지막 프로그램이 done를 출력합니다.

    Promise 실행 순서


    Promise가 생성되면 바로 실행됩니다.하지만 약속.then의 방법은 호출 주기가 지나면 다시 호출됩니다. 다음 예를 보겠습니다.
    
    let promise = new Promise(((resolve, reject) => {
     console.log('Step1');
     resolve();
    }));
    
    promise.then(() => {
     console.log('Step3');
    });
    
    console.log('Step2');
    출력
    Step1
    Step2
    Step3

    async와 await


    Promise는 물론 좋습니다. 지옥을 체인 호출로 바꾸겠습니다.우리는 then으로 여러 개의 Promise를 연결합니다. 이전의promiseresolve의 결과는 다음promise에서 then의 매개 변수입니다.
    체인 호출은 어떤 결점이 있습니까?
    예를 들어 우리는promise에서resolve의 값을 가지고 이 값에 따라 업무 논리적인 처리를 해야 한다.
    만약 이 업무 논리가 매우 길다면, 우리는 다음then에 매우 긴 업무 논리 코드를 써야 한다.이렇게 하면 우리의 코드가 매우 번거로워 보인다.
    그럼 프로미스에서 리졸브의 결과를 직접 되돌릴 수 있는 방법은 없을까요?
    정답은 바로await입니다.
    promise 앞에await를 추가할 때, 호출된 코드는promise가 해결되거나 거부될 때까지 멈춥니다.
    await는 반드시 async 함수에 넣어야 한다는 것을 주의하십시오. 우리는 async와 await의 예를 보겠습니다.
    
    const logAsync = () => {
     return new Promise(resolve => {
     setTimeout(() => resolve(' '), 5000)
     })
    }
    위에서 우리는 logasync 함수를 정의했습니다. 이 함수는 Promise를 되돌려줍니다. 이 Promise 내부는 setTimeout을 사용하여 Resolve를 하기 때문에 우리는 이를 비동기적으로 볼 수 있습니다.
    await를 사용하여 resolve의 값을 얻으려면 async 함수에 넣어야 합니다.
    
    const doSomething = async () => {
     const resolveValue = await logAsync();
     console.log(resolveValue);
    }

    async의 실행 순서


    await는 실제적으로promise의resolve결과를 기다리는 것입니다. 우리는 위의 예를 결합합니다.
    
    const logAsync = () => {
     return new Promise(resolve => {
      setTimeout(() => resolve(' '), 1000)
     })
    }
    
    const doSomething = async () => {
     const resolveValue = await logAsync();
     console.log(resolveValue);
    }
    
    console.log('before')
    doSomething();
    console.log('after')
    위의 예 출력:
    before
    after
    작은 마형
    알 수 있듯이aysnc는 비동기적으로 실행되고, 그 순서는 현재 이 주기 이후이다.

    async의 특징


    async는 뒤에 연결된 모든 함수를 Promise로 변환합니다. 뒤에 있는 함수가 Promise로 되돌아오지 않아도 됩니다.
    
    const asyncReturn = async () => {
     return 'async return'
    }
    
    asyncReturn().then(console.log)
    Promise만 뒤에서 then을 받을 수 있기 때문에, 우리는 async가 일반적인 함수를 하나의 Promise로 봉인했다는 것을 알 수 있다.
    
    const asyncReturn = async () => {
     return Promise.resolve('async return')
    }
    
    asyncReturn().then(console.log)

    총결산


    promise는 지옥을 피했습니다. 이것은 콜백 inside 콜백을 then의 체인 호출 형식으로 바꿨습니다.
    그러나 체인 호출은 읽기와 디버깅이 불편하다.그래서 async와await가 생겼어요.
    async와await는 체인 호출을 유사한 프로그램 순서로 실행하는 문법으로 바꾸어 더욱 쉽게 이해하고 디버깅할 수 있습니다.
    Promise를 사용하는 경우를 비교해 보겠습니다.
    
    const getUserInfo = () => {
     return fetch('/users.json') //  
     .then(response => response.json()) //   JSON
     .then(users => users[0]) //  
     .then(user => fetch(`/users/${user.name}`)) //  
     .then(userResponse => userResponse.json()) //   JSON
    }
    
    getUserInfo()
    async와 await로 바꾸기:
    
    const getUserInfo = async () => {
     const response = await fetch('/users.json') //  
     const users = await response.json() //   JSON
     const user = users[0] //  
     const userResponse = await fetch(`/users/${user.name}`) //  
     const userData = await userResponse.json() //   JSON
     return userData
    }
    
    getUserInfo()
    업무 논리가 더욱 뚜렷해지는 것을 볼 수 있다.동시에, 우리는 많은 중간 값을 얻었는데, 이렇게 하면 우리가 디버깅을 하는 것도 편리하다.
    이 노드js의 비동기 프로그래밍 지식에 대한 상세한 설명은 여기까지 소개합니다. 더 많은 노드js의 비동기 프로그래밍 내용을 깊이 있게 이해하려면 저희 이전의 글을 검색하거나 아래의 관련 글을 계속 훑어보십시오. 앞으로 많은 응원 부탁드립니다!

    좋은 웹페이지 즐겨찾기