0 에서 Promise 를 실현 하 다.

17205 단어 자바 scriptpromise
1. Promise / A + 규범
① Promise 는 하나의 클래스 또는 함수 로 내부 에 3 개의 상태 가 있 는데 각각 pending (대기), fulfilled (실행, 완성), rejected (거부, 미 완성) 이다.기본 값 은 pending 상태 입 니 다. 즉, Promise 대상 이 처음 만 들 어 졌 을 때 상 태 는 pending 이 고 pending 상 태 는 fulfilled 또는 rejected 로 전환 할 수 있 습 니 다.fulfilled 와 rejected 는 최종 상태 로 fulfilled 나 rejected 로 바 뀌 면 다른 상태 로 바 꿀 수 없습니다.
② Promise 는 대외 적 으로 then 방법 을 제공 해 야 한다.
promise.then(onFulfilled, onRejected)

선택 가능 한 인자 인 onFulfilled 와 onRejected 가 함수 가 아 닐 때 무시 해 야 합 니 다.
onFulfilled 와 onRejected 함 수 는 모두 비동기 로 실행 되 어야 합 니 다.
onFulfilled 함 수 를 호출 할 때 현재 Promise 의 값 을 매개 변수 로 입력 하고 한 번 만 호출 할 수 있 습 니 다.
onRejected 함 수 를 호출 할 때 현재 Promise 의 실패 원인 을 매개 변수 로 입력 하고 한 번 만 호출 할 수 있 습 니 다.
then 함수 의 반환 값 은 체인 호출 을 위해 Promise 입 니 다.
③ resolve Promisethen 방법 은 Promise 대상 을 만 들 고 되 돌려 줍 니 다. then 에 등 록 된 리 셋 함 수 는 여러 값 을 되 돌려 줍 니 다. 반드시 검증 해 야 합 니 다.
then 방법 이 되 돌아 오 는 promise 는 then 에서 되 돌아 오 는 함수 반환 값 x 와 같 을 수 없습니다. 그렇지 않 으 면 오 류 를 던 져 야 합 니 다.
then 반전 함수 반환 값 이 비 Promise 대상 이 라면 then 에서 돌아 오 는 promise 대상 의 resolve 방법, resolve (x) 를 사용 하면 됩 니 다.
then 리 셋 함수 가 값 x 를 Promise 대상 또는 then 방법 이 있 는 대상 이나 함수 로 되 돌려 준다 면 then 방법 으로 리 셋 을 등록 하고 Promise 나 클래스 Promise 대상 의 값 을 then 이 되 돌아 오 는 promise 의 값 으로 가 져 가 야 합 니 다. 값 이 Promise 대상 이 라면 재 귀 작업 이 필요 합 니 다.
2. Promise 실현
① 첫 번 째 규범 에 따 르 면 Promise 는 하나의 클래스 나 함수 이기 때문에 우 리 는 먼저 Promise 를 하나의 클래스 로 정의 하고 내부 에 세 가지 상태 가 있 으 며 우 리 는 이 를 상수 로 정의 한다.
var PENDING = "pending"; //     
var FULFILLED = "fulfilled"; //   、    
var REJECTED = "rejected"; //   、     
class Promise {
    constructor() {
        this.state = PENDING; // Promise              
    }
}

② 저 희 는 Promise 를 만 들 때 하나의 함수 가 들 어 옵 니 다. 이 함 수 는 Promise 대상 을 만 들 때 바로 실 행 됩 니 다. 또한 현재 Promise 대상 을 실행 하거나 거부 하 는 두 개의 인 자 를 받 습 니 다. 즉, 현재 Promise 대상 의 상 태 를 수정 하 는 것 입 니 다.Promise 는 비동기 처리 에 사용 되 기 때문에 Promise 상태 가 완 료 될 때 비동기 작업 이 실 행 된 결 과 를 받 을 수 있 고 Promise 상태 가 완료 되 지 않 았 을 때 실패 원인 을 받 을 수 있 기 때문에 Promise 내부 에 비동기 작업 의 결과 value, 실패 원인 reason 을 저장 해 야 합 니 다.
......
class Promise {
    constructor(executor) { //        
        ......
        this.value = undefined; //          
        this.reason = undefined; //        
        const resolve = (value) => {
            this.value = value;
            this.state = FULFILLED; //  Promise           
        }
        const reject = (reason) => {
            this.reason = reason;
            this.state = REJECTED; //  Promise            
        }
        try {
            executor(resolve, reject); //                ,        
        } catch(e) {
            reject(e);
        }
    }
}

③ 여기에 또 하나의 문제 가 존재 한다. 바로 Promise 는 반드시 한 번 에 실행 되 어야 한 다 는 것 이다. Promise 의 상 태 는 pending 상태 에서 fulfilled 또는 rejected 로 바 뀌 면 더 이상 변화 가 일어나 지 않 고 fulfilled 에서 fulfilled 로 바 뀌 어도 안 된다. 즉, resolve 또는 reject 는 한 번 만 수행 할 수 있다 는 것 이다.따라서 resolve 와 reject 내 부 를 판단 해 야 합 니 다. 상태 가 바 뀌 었 다 면 더 이상 실행 하지 않 습 니 다. 예 를 들 어:
......
class Promise {
    constructor(executor) { //        
        ......
        const resolve = (value) => {
            if (this.state === PENDING) { //       resolve,    resolve  
                ......
            }
        }
        const reject = (reason) => {
            if (this.state === PENDING) { //       reject
                ......
            }
        }
        ......
    }
}

④ Promise 에 then 함 수 를 추가 합 니 다. then 함 수 는 onFulfilled, onRejected 두 함 수 를 매개 변수 로 하여 Promise 완료 시 와 미 완성 시의 반전 함 수 를 처리 합 니 다. 함수 가 아니라면 하나의 함수 로 초기 화 합 니 다. 예 를 들 어:
class Promise {
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => { //   onFulfilled    ,            
            return value;
        };
        onRejected = typeof onRejected === "function" ? onRejected : (reason) => { //   onRejected    ,             
            throw reason; //         
        }
    }
}

⑤ then 방법 은 리 셋 을 등록 하 는 과정 입 니 다. then 을 호출 한 이 Promise 대상 의 상태 가 완성 상태 로 바 뀌 면 onFulfilled 리 셋 함 수 를 실행 할 수 있 습 니 다. Promise 대상 의 상태 가 거부 상태 로 바 뀌 면 onRejected 리 셋 함 수 를 실행 할 수 있 습 니 다.그래서 리 셋 함수 의 실행 은 then 을 호출 하 는 Promise 상태 에 의존 합 니 다.또한 체인 호출 을 지원 하기 위해 then 방법 은 Promise 대상 을 되 돌려 야 합 니 다.앞의 Promise 규범 에 따라 들 어 오 는 반전 함 수 는 비동기 로 실행 되 어야 합 니 다. 여 기 는 setTimeout 으로 시 뮬 레이 션 을 합 니 다.
class Promise {
    then(onFulfilled, onRejected) {
        ......
        let promise;
        switch(this.state) {
            case FULFILLED: //   then     ,  Promise          ,              
                promise = new Promise((resolve, reject) => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value);
                        } catch(e) {
                            console.log(e); //       
                            reject(e);
                        }
                    });
                });
                break;
            case REJECTED:
                promise = new Promise((resolve, reject) => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason);
                        } catch(e) {
                            reject(e);
                        }
                    });
                }
                break;
            case PENDING:
                promise = new Promise((resolve, reject) => {
                    // TODO
                });
                break;
        }
        return promise;
    }
}

⑥ then 의 Promise 대상 이 pending 상태 에 있 을 때, then 을 통 해 등 록 된 리 셋 함 수 는 즉시 실행 되 지 않 으 며, Promise 의 상태 가 최종 상태 로 변 할 때 까지 기 다 려 야 등 록 된 리 셋 함 수 를 실행 할 수 있 습 니 다.여기에 게시 구독 모드 가 포함 되 어 있다.우 리 는 먼저 리 셋 함 수 를 저장 할 수 있 습 니 다. 그러면 언제 Promise 가 최종 상태 가 될 수 있 습 니까?바로 resolve 나 reject 를 호출 할 때 입 니 다. 따라서 resolve 나 reject 를 호출 할 때 등 록 된 리 셋 함 수 를 꺼 내 실행 하면 됩 니 다.
class Promise {
    constructor(executor) {
        const resolve = (value) => {
            if (this.state === PENDING) { //       resolve,    resolve  
                ......
                this.onFulfilleds.forEach(fn => fn()); //   then             
            }
        };
        const reject = (reason) => {
            if (this.state === PENDING) { //       reject
                ......
                this.onRejecteds.forEach(fn => fn()); //   then             
            }
        };
    }
    then(onFulfilled, onRejected) {
        ......
        switch(this.state) {
            case PENDING:
                promise = new Promise((resolve, reject) => {
                    this.onFulfilleds.push(() => {
                        try {
                            let x = onFulfilled(this.value);
                        } catch(e) {
                            console.log(e); //       
                            reject(e);
                        }
                    });
                    this.onRejecteds.push(() => {
                        try {
                            let x = onRejected(this.reason);
                        } catch(e) {
                            reject(e);
                        }
                    });
                });
                break;
        }
    }
}

⑦ 다음은 then 이 등록 한 리 턴 함수 의 리 턴 값 을 처리 하 는 것 입 니 다. 리 턴 함수 의 리 턴 값 은 여러 가지 상황 일 수 있 기 때문에 일반적인 값 일 수도 있 고 Promise 대상 일 수도 있 고 then 방법 을 가 진 대상 일 수도 있 으 므 로 일일이 처리 해 야 합 니 다.여기 서 우 리 는 하나의 단독 방법 으로 resolve Promise () 를 사용 하여 여러 가지 상황 을 처리 합 니 다. 예 를 들 어:
//   then()      Promise  ,        x,then()      Promise resolve、reject
const resolvePromise = function(promise, x, resolve, reject) {
    // TODO
}
class Promise {
    constructor(executor) { //        
        ......
    }
    then(onFulfilled, onRejected) {
        case FULFILLED:
            promise = new Promise((resolve, reject) => {
                ......
                let x = onFulfilled(this.value);
                resolvePromise(promise, x, resolve, reject); //           
            });
        case REJECTED:
            promise = new Promise((resolve, reject) => {
                ......
                let x = onRejected(this.reason);
                resolvePromise(promise, x, resolve, reject); //           
            });
        case PENDING:
            this.onFulfilleds.push(() => {
                let x = onFulfilled(this.value);
                resolvePromise(promise, x, resolve, reject); //           
            });
            this.onRejecteds.push(() => {
                let x = onRejected(this.reason);
                resolvePromise(promise, x, resolve, reject); //           
            });
    }
}

3. resolvePromise 실현
① 리 턴 함수 리 턴 값 이 then () 방법 에서 만 든 Promise 대상 과 같 으 면 오 류 를 던 집 니 다. 이 는 자신 이 죽 을 때 까지 기다 리 는 것 과 같 습 니 다.
let p1 = new Promise((resolve, reject) => {
    resolve(1);
})
let p2 = p1.then((value) => { // p2 then     Promise  
    return p2;
});
//       ,  Chaining cycle detected for promise #
const resolvePromise = function(promise, x, resolve, reject) {
    if (promise === x) { //   resolve  
        throw new Error("Chaining cycle detected for promise #");
    }
}

② 리 셋 함수 가 Promise 대상 또는 then 방법 을 가 진 클래스 Promise 대상 또는 하나의 함수 로 되 돌아 오 면 함수 에 then 방법 이 있 을 수 있 으 므 로 then 방법 을 꺼 내 실행 해 야 합 니 다. Promise 대상 에 게 then 방법의 실행 은 해당 하 는 리 셋 함 수 를 등록 합 니 다.Promise 상태 가 최종 상태 로 바 뀌 면 대응 하 는 리 셋 함 수 를 실행 하고 리 셋 함 수 를 실행 하면 Promise 대상 의 value 값 을 받 은 다음 이 value 값 을 then 방법 으로 만 든 Promise 대상 의 value 값 으로 호출 합 니 다.
const resolvePromise = function(promise, x, resolve, reject) {
    ......
    if ((x && typeof x === "object") || typeof x === "function") { //          ,      then  
        let executed;
        try {
            let then = x.then; //     then  
            if (typeof then === "function") { //         then  ,    Promise      then     
                then.call(x, function (y) { //   then  ,     Promise  ,      ,       ,       ,       Promise value 
                    if (executed) return;
                    executed = true; //              
                    resolvePromise(promise, y, resolve, reject); //          Promise  ,              
                }, function (e) {
                    if (executed) return;
                    executed = true;
                    reject(e);
                });
            } else { //    then       ,  resolve  
                resolve(x);      
            }
        } catch(e) {
            if (executed) return;
            executed = true;
            reject(e);
        }
    } else {
        resolve(x);
    }
}

4. catch 실현
catch 는 특수 한 then 방법 으로 볼 수 있 습 니 다. 내부 에서 then () 방법 을 호출 하지만 거부 하 는 리 셋 함수 만 등록 합 니 다. 이것 이 바로 then (onFulfilled, onRejected) 과 then (onFulfilled). catch (onRejected) 의 차이 입 니 다. onRejected 를 then 에 쓰 면 then 의 onFulfilled 에 오류 가 발생 했 을 때 onRejected 는 오 류 를 포착 할 수 없습니다.catch 에 쓰 면 다음 then () 방법 에 해당 하기 때문에 이전 then () 방법 에서 발생 한 오 류 를 포착 할 수 있 습 니 다.
class Promise {
    catch(onRejected) {
        return this.then(null, onRejected); //           
    }
}

총화
Promise 는 내부 에 state, value, reason 등 속성 이 있 는데 각각 현재 Promise 의 상태, 실행 성공 후의 반환 값 을 저장 하 는 데 사용 되 고 실행 실패 의 원인 이 되 며 내부 에 resolve, reject 두 가지 방법 도 제공 된다. 이 두 가지 방법 은 Promise 의 상 태 를 수정 하기 위해 매개 변수 형식 으로 실행 기, 즉 외부 로 전달 된다.Promise 는 리 셋 함 수 를 등록 하 는 데 사용 되 는 then 방법 도 제공 했다. 리 셋 을 등록 할 때 현재 Promise 의 상태 와 관련 이 있 고 최종 상태 라면 즉시 실행 하 며 대기 상태 라면 저장 하고 resolve 나 reject 방법 을 호출 할 때 리 셋 을 꺼 내 실행 하 는 것 이다.등 록 된 리 셋 함 수 는 여러 가지 값 을 되 돌려 줄 수 있 습 니 다. 일반 값 으로 되 돌아 오 면 then 에서 돌아 오 는 Promise 의 resolve 방법 으로 resolve 를 사용 하면 됩 니 다.Promise 대상 이나 then 방법 이 있 는 대상 이나 함 수 를 되 돌려 준다 면 then 방법 을 호출 하고 현재 Promise 의 값 을 받 기 위해 사용자 정의 리 셋 을 등록 해 야 합 니 다. 이 Promise 가 최종 상태 로 바 뀌 면 리 셋 을 실행 하면 value 를 받 을 수 있 습 니 다. 마지막 으로 then 이 되 돌아 오 는 Promise 의 value, 즉 resolve (x) 로 사용 할 수 있 습 니 다.전체 원본 코드 는 다음 과 같 습 니 다.
var PENDING = "pending"; //     
var FULFILLED = "fulfilled"; //   、    
var REJECTED = "rejected"; //   、     
//   then()      Promise  ,        x,then()      Promise resolve、reject
const resolvePromise = function(promise, x, resolve, reject) {
    if (promise === x) { //   resolve  
        throw new Error("Chaining cycle detected for promise #");
    }
    if ((x && typeof x === "object") || typeof x === "function") { //          ,      then  
        let executed;
        try {
            let then = x.then; //     then  
            if (typeof then === "function") { //         then  ,    Promise      then     
                then.call(x, function (y) { //   then  ,     Promise  ,      ,       ,       ,       Promise value 
                    if (executed) return;
                    executed = true; //              
                    resolvePromise(promise, y, resolve, reject); //          Promise  ,              
                }, function (e) {
                    if (executed) return;
                    executed = true;
                    reject(e);
                });
            } else { //    then       ,  resolve  
                resolve(x);
            }
        } catch(e) {
            if (executed) return;
            executed = true;
            reject(e);
        }
    } else {
        resolve(x);
    }
}
class Promise {
    constructor(executor) { //        
        this.state = PENDING; // Promise              
        this.value = undefined; //          
        this.reason = undefined; //        
        this.onFulfilleds = []; //   then          
        this.onRejecteds = []; //   then          
        const resolve = (value) => {
            if (this.state === PENDING) { //       resolve,    resolve  
                this.value = value;
                this.state = FULFILLED; //  Promise           
                this.onFulfilleds.forEach(fn => fn()); //   then             
            }
        };
        const reject = (reason) => {
            if (this.state === PENDING) { //       reject
                this.reason = reason;
                this.state = REJECTED; //  Promise            
                this.onRejecteds.forEach(fn => fn()); //   then             
            }
        };
        try {
            executor(resolve, reject); //                ,        
        } catch(e) {
            reject(e);
        }
    }
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => { //   onFulfilled    ,            
            return value;
        };
        onRejected = typeof onRejected === "function" ? onRejected : (reason) => { //   onRejected    ,             
            throw reason; //         
        }
        let promise;
        switch(this.state) {
            case FULFILLED: //   then     ,  Promise          ,              
                promise = new Promise((resolve, reject) => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value);
                            resolvePromise(promise, x, resolve, reject);
                        } catch(e) {
                            console.log(e);
                            reject(e);
                        }
                    });
                });
                break;
             case REJECTED:
                promise = new Promise((resolve, reject) => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason);
                            resolvePromise(promise, x, resolve, reject);
                        } catch(e) {
                            reject(e);
                        }
                    });
                });
                break;
            case PENDING:
                promise = new Promise((resolve, reject) => {
                    this.onFulfilleds.push(() => {
                        try {
                            let x = onFulfilled(this.value);
                            resolvePromise(promise, x, resolve, reject);
                        } catch(e) {
                            reject(e);
                        }
                    });
                    this.onRejecteds.push(() => {
                        try {
                            let x = onRejected(this.reason);
                            resolvePromise(promise, x, resolve, reject);
                        } catch(e) {
                            reject(e);
                        }
                    });
                });
                break;
            }
        return promise;
    }
    catch(onRejected) {
        return this.then(null, onRejected); //           
    }
}

좋은 웹페이지 즐겨찾기