산채 하나의 Promise

약간의 깨 달 음.
Promise 는 이 보 를 작성 하 는 또 다른 방식 입 니 다. 저 는 어 리 석 습 니 다. 바로 Callback 의 패키지 입 니 다.
Callback 에 비해 다음 과 같은 특징 이 있 습 니 다.
  • Promise 는 비동기 결 과 를 저장 하여 수시로 가 져 올 수 있 습 니 다
  • 체인 호출 then 방법 은 새로운 Promise 로 돌아 가 지옥 으로 돌아 가 는 것 을 피한다
  • 한 번 의 이 보 를 결정 하 는 데 는 두 가지 부분 이 있다.
  • 비동기 사건 발기
  • 비동기 처리 결과
  • Promise 는 비동기 이벤트 에 여러 개의 처리 함 수 를 등록 하고 밤 을 들 수 있 습 니 다. 이렇게.
    let p1 = new Promise((resolve) => {
      fs.readFile('./test.js', 'utf8', (err, data) => {
        resolve(data)
      })
    })
    p1.then(data => console.log(data))
    p1.then(data => console.log(data.toUpperCase()))

    Callback 으로 똑 같은 효 과 를 낼 수 있 습 니 다.
  • 등 록 된 모든 함 수 를 콜 백 스 로 저장 합 니 다
  • 비동기 이벤트 가 결 과 를 되 돌려 주 고 콜 백 스 를 옮 겨 다 니 며 모든 등 록 된 함수
  • 를 순서대로 실행 합 니 다.
    이렇게
    let callbacks = []
    function resolve(data){
      callbacks.forEach(cb => cb(data))
    }
    
    fs.readFile('./test.js', 'utf8', (err, data) => {
      resolve(data)
    })
    
    callbacks.push(data => console.log(data))
    callbacks.push(data => console.log(data.toUpperCase()))

    상기 코드 를 밀봉 하 다.
    const fs = require('fs')
    
    class FakePromise {
      constructor(fn){
          this.callbacks = []
          resolve = resolve.bind(this)
        function resolve(data){
          this.callbacks.forEach(cb => cb(data))
        }
        fn(resolve)
      }
      
      then(onFulfilled){
        this.callbacks.push(onFulfilled)
      }
    }
    
    let p1 = new FakePromise(resolve => {
      fs.readFile('./test.js', 'utf8', (err, data) => {
        resolve(data)
      })
    })
    p1.then(data => console.log(data))
    p1.then(data => console.log(data.toUpperCase()))

    하?진짜 Promise 랑 비슷 하지 않 아 요?
    게시 - 구독 모드 의 측면 에서 볼 때:
  • Fake Promise 에서 .then(onFulfilled) 메 시 지 를 구독 하고 비동기 결 과 를 등록 처리 하 는 함수
  • resolve(data) 을 통 해 메 시 지 를 발표 하고 비동기 결 과 를 처리 하 는 함 수 를 실행 합 니 다. 발표 시 기 는 비동기 사건 이 완 료 될 때
  • 입 니 다.
    지연 해결
    이전 코드 에 문제 가 있 습 니 다. 실행 p1.then(data => console.log(data)) 하기 전에 resolve(data) 이미 실행 되 었 다 면 .then(onFulfilled) 등 록 된 비동기 결 과 를 처리 하 는 함 수 는 영원히 실행 되 지 않 을 것 입 니 다.
    이러한 상황 을 피하 기 위해 resolve 함 수 를 개조 하고 내부 에 setTimeout 을 추가 하여 등 록 된 처리 함수 가 다음 이벤트 대기 열 에서 실 행 될 수 있 도록 합 니 다. 이렇게
    function resolve(value) {
        setTimeout(() => {
            this.callbacks.forEach(cb => cb(value))
        }, 0)
    }

    resolve 내부 함수 지연 을 통 해 먼저 구독 메 시 지 를 확보 하고 메 시 지 를 발표 합 니 다.
    그러나 Promise 는 메 시 지 를 발표 한 후에 도 메 시 지 를 구독 하고 즉시 실행 할 수 있 는 추가 기능 도 있다.
    const fs = require('fs')
    
    let p1 = new Promise(resolve => {
        fs.readFile('./test.js', 'utf8', (err, data) => resolve(data))
    })
    
    p1.then(data => console.log(data))
    setTimeout(function(){
        p1.then(data => console.log(data.toUpperCase()))
    }, 5000)

    5s 내 에 파일 을 읽 는 데 성 공 했 지만 5s 이후 에 도 .then 등록 을 통 해 사건 을 처리 할 수 있 고 이 사건 은 즉시 실 행 될 것 입 니 다.
    먼저 발표 하고 구독 하 다
    먼저 발표 하고 구독 하 는 기 초 는 메 시 지 를 저장 하 는 것 이다.그 다음 에 상 태 를 기록 하고 메시지 가 발표 되 었 는 지 판단 해 야 합 니 다. 만약 에 메 시 지 를 발표 하지 않 으 면 .then 을 통 해 리 셋 을 등록 할 때 리 셋 함 수 를 내부 리 셋 대기 열 에 추가 합 니 다.메시지 가 발표 되면 .then 를 통 해 리 셋 을 등록 할 때 리 셋 함수 에 메 시 지 를 직접 전달 하고 실행 합 니 다.
    Promise 규범 에서 사용 하 는 상태 체 제 는 pending, fulfilled, rejected 이다.pendingfulfilled 또는 rejected 로 전환 할 수 있 고 한 번 만 전환 할 수 있다.fulfilledrejected 로 전환 하면 상 태 는 더 이상 변 할 수 없다.
    수정 코드 는 다음 과 같 습 니 다.
    class FakePromise {
        constructor(fn) {
            this.value = null
            this.state = 'pending'
            this.callbacks = []
            resolve = resolve.bind(this)
    
            function resolve(value) {
                setTimeout(() => {
                    this.value = value
                    this.state = 'fulfilled'
                    this.callbacks.forEach(cb => cb(value))
                }, 0)
            }
            fn(resolve)
        }
    
        then(onFulfilled) {
            if (this.state === 'pending') {
                this.callbacks.push(onFulfilled)
            } else {
                onFulfilled(this.value)
            }
        }
    }

    먼저 발표 하고 구독 하 는 것 이 이 루어 졌 으 니 resolve 의 setTimeout 은 지 울 수 있 지 않 습 니까?
    안 돼 요. 진지 한 Promise 가 그 렇 기 때 문 이에 요.
    let p1 = new Promise(resolve => {
        resolve('haha')
    })
    p1.then(data => console.log(data))
    p1.then(data => console.log(data.toUpperCase()))
    console.log('xixi')
    // xixi
    // haha
    // HAHA

    Resolve 에서 setTimeout 을 유지 해야만 Fake Promise 가 같은 효 과 를 낼 수 있 습 니 다.
    let p1 = new FakePromise(resolve => {
        resolve('haha')
    })
    p1.then(data => console.log(data))
    p1.then(data => console.log(data.toUpperCase()))
    console.log('xixi')
    // xixi
    // haha
    // HAHA

    setTimeout 의 출력 결과 가 없습니다.
    // haha
    // HAHA
    // xixi

    체인 프로 미스
    진지 한 Promise 는 연쇄 적 으로 호출 되 어 지옥 으로 돌아 가 는 것 을 피 할 수 있다.
    let p1 = new Promise(resolve => {
        fs.readFile('./test.js', 'utf8', (err, data) => {
            resolve(data)
        })
    }).then(res => {
        return new Promise(resolve => {
            fs.readFile('./main.js', 'utf8', (err, data) => {
                resolve(data)
            })
        })
    }).then(res => {
        console.log(res)
    })

    진지 한 Promise 호출 then 방법 은 새로운 Promise 대상 을 되 돌려 줍 니 다.
    우리 가 위조 한 Fake Promise 는 이 기능 을 실현 하지 못 했 습 니 다. 원래 의 then 방법 입 니 다.
    ...
        then(onFulfilled){
            if (this.state === 'pending') {
                this.callbacks.push(onFulfilled)
            } else {
                onFulfilled(this.value)
            }
        }
    ...

    원래 의 then 방법 은 state 에 따라 onFulfilled 함 수 를 등록 하 는 지, 아니면 onFulfilled 함 수 를 실행 하 는 지 판단 하 는 것 입 니 다.
    Fake Promise 의 높 은 모방 을 실현 하기 위해 서 는 then 방법 을 개조 하여 새로운 Fake Promise 로 되 돌려 야 합 니 다. 쉽게 구분 하기 위해 돌아 오 는 Fake Promise 를 SonFake Promise 라 고 지 었 고, 이전에 then 대상 을 Father Fake Promise 로 호출 했 습 니 다.
    그러면 문제 가 왔 습 니 다.
  • 그러면 이 SonFake Promise 를 구성 하 는 함수 매개 변 수 는 무엇 입 니까
  • 이 SonFakePromise 는 언제 해결 합 니까?

  • 우선, 새로운 SonFake Promise 를 만 들 때 들 어 오 는 함수 인자 fn 을 한 번 실행 하고 이 함수 에는 resolve 인자 가 있 습 니 다.
    ...
        then(onFulfilled){
          if(this.state === 'pending'){
            this.callbacks.push(onFulfilled)
            let SonFakePromise = new FakePromise(function fn(resolve){
              
            })
            return SonFakePromise
          }else{
            onFulfilled(this.value)
            let SonFakePromise = new FakePromise(function fn(resolve){
              
            })
            return SonFakePromise
          }
        }
    ...

    지금 문 제 는 이 SonFake Promise 가 언제 해 결 됩 니까?즉, 구조 함수 중의 함수 매개 변수 fn 은 어떻게 정의 합 니까?
    진지 한 Promise 의 예 를 결합 해 보면
    let faherPromise = new Promise(resolve => {
        fs.readFile('./test.js', 'utf8', (err, data) => {
            resolve(data)
        })
    }).then(res => {
        return new Promise(resolve => {
            fs.readFile('./main.js', 'utf8', (err, data) => {
                resolve(data)
            })
        })
    }).then(res => {
        console.log(res)
    })
    //    
    let faherPromise = new Promise(resolve => {
        fs.readFile('./test.js', 'utf8', (err, data) => {
            resolve(data)
        })
    })
    let sonPromise = faherPromise.then(function onFulfilled(res){
        return new Promise(function fn(resolve){
            fs.readFile('./main.js', 'utf8', (err, data) => {
                resolve(data)
            })
        })
    }).then(res => {
        console.log(res)
    })

    예 에서 onFulfilled 함 수 는 다음 과 같 으 며, 실행 후 새로운 Promise 로 되 돌아 가 fulPromise 라 고 잠시 이름 을 지 었 습 니 다.
    function onFulfilled(res) {
      return new Promise(function fn(resolve){
        fs.readFile('./main.js', 'utf8', (err, data) => {
          resolve(data)
        })
      })
    }

    이제 father Promise, son Promise 와 fulPromise 의 관 계 를 분석 해 보 겠 습 니 다.
  • sonPromise 는 father Promise 의 then 방법 을 호출 하여 되 돌려 줍 니 다
  • 이 then 방법 을 호출 하려 면 함수 인 자 를 입력 해 야 합 니 다. retFul Promise
  • 라 고 이름 을 지 었 습 니 다.
  • retFulPromise 함수 가 실행 한 반환 값 fulPromise
  • 아래 코드 가 이해 에 도움 이 되 었 으 면 좋 겠 습 니 다.
    let fatherPromise = new Promise(function fatherFn(fatherResolve){
      fs.readFile('./test.js', 'utf8', (err, data) => {
        fatherResolve(data)
      })
    })
    
    let sonPromise = fatherPromise.then(retFulPromise)
    
    function retFulPromise(res) {
      let fulPromise = new Promise(function fulFn(fulResolve){
        fs.readFile('./main.js', 'utf8', (err, data) => {
          fulResolve(data)
        })
      })
      return fulPromise
    }

    father Promise 의 상태 가 fulfilled 일 때 retFulPromise 를 실행 하고 fulPromise 로 돌아 갑 니 다. 이 fulPromise 가 fulResolve 를 실행 할 때 main. js 읽 기 가 완료 되면 sonPromise 도 내부 resolve 를 실행 합 니 다.
    그래서 sonPromise 의 sonResolve 함수 도 fulPromise 에 등록 되 었 다 고 볼 수 있 습 니 다.
    So, 전체 절 차 를 알 게 되 었 습 니 다. 자신의 Fake Promise 를 어떻게 수정 해 야 합 니까?
    쇼 조작, 기 교 를 시험 할 때 가 되 었 습 니 다. sonResolve 의 인용 을 저장 하고 fulFake Promise 에 등록 합 니 다.
    const fs = require('fs')
    
    class FakePromise {
        constructor(fn) {
            this.value = null
            this.state = 'pending'
            this.callbacks = []
            resolve = resolve.bind(this)
    
            function resolve(value) {
                setTimeout(() => {
                    this.value = value
                    this.state = 'fulfilled'
                    this.callbacks.forEach(cb => {
                        let returnValue = cb.onFulfilled(value)
                        if (returnValue instanceof FakePromise) {
                            returnValue.then(cb.sonResolveRes)
                        }
                    })
                })
            }
            fn(resolve)
        }
    
        then(onFulfilled) {
            if (this.state === 'pending') {
                let sonResolveRes = null
                let sonFakePromise = new FakePromise(function sonFn(sonResolve) {
                    sonResolveRes = sonResolve
                })
                this.callbacks.push({
                    sonFakePromise,
                    sonResolveRes,
                    onFulfilled
                })
                return sonFakePromise
            } else {
                let value = onFulfilled(this.value)
                let sonResolveRes = null
                let sonFakePromise = new FakePromise(function sonFn(sonResolve) {
                    sonResolveRes = sonResolve
                })
                if (value instanceof FakePromise) {
                    value.then(sonResolveRes)
                }
                return sonFakePromise
            }
        }
    }

    다각도 테스트
    let fatherFakePromise = new FakePromise(resolve => {
        fs.readFile('./test.js', 'utf8', (err, data) => {
            resolve(data)
        })
    })
    let sonFakePromise = fatherFakePromise.then(function onFulfilled(res) {
        return new FakePromise(function fn(resolve) {
            fs.readFile('./main.js', 'utf8', (err, data) => {
                resolve(data)
            })
        })
    }).then(res => {
        console.log(res)
    })
    let fatherFakePromise = new FakePromise(resolve => {
        fs.readFile('./test.js', 'utf8', (err, data) => {
            resolve(data)
        })
    })
    setTimeout(function () {
        let sonFakePromise = fatherFakePromise.then(function onFulfilled(res) {
            return new FakePromise(function fn(resolve) {
                fs.readFile('./main.js', 'utf8', (err, data) => {
                    resolve(data)
                })
            })
        }).then(res => {
            console.log(res)
        })
    }, 1000)
    let fatherFakePromise = new FakePromise(resolve => {
        resolve('haha')
    })
    
    let sonFakePromise = fatherFakePromise.then(function onFulfilled(res) {
        return new FakePromise(function fn(resolve) {
            fs.readFile('./main.js', 'utf8', (err, data) => {
                resolve(data)
            })
        })
    }).then(res => {
        console.log(res)
    })
    let fatherFakePromise = new FakePromise(resolve => {
        resolve('haha')
    })
    
    setTimeout(function () {
        let sonFakePromise = fatherFakePromise.then(function onFulfilled(res) {
            return new FakePromise(function fn(resolve) {
                fs.readFile('./main.js', 'utf8', (err, data) => {
                    resolve(data)
                })
            })
        }).then(res => {
            console.log(res)
        })
    }, 1000)

    참고 자료
  • 30 분, Promise 원리 철저히 알 게 해 줄 게
  • 좋은 웹페이지 즐겨찾기