Promise原理分析

Promise成为你们解决异步回调地狱问题的重要方法,理解它的原理和底层实现有助于咱们更加合理的使用。如下遵循PromiseA+规范实现了一个简单的Promise.jquery

实现分为六步:数组

  • 实现同步promise
  • 实现异步promise和屡次then
  • 实现promise的链式调用
  • 特殊状况的处理
  • 实现then穿透
  • 测试

实现同步的promise

  • 明确promise有三个状态: pending fulfilled rejectedpromise

  • 在new Promise时, excutor会当即执行bash

  • 每一个promise上会有一个then方法, then方法中传递两个参数onFulfilled和onRejected异步

代码以下:测试

class Promise{
    constructor(excutor){
        this.status = 'pending'; // 默认是等待态
        this.value = undefined;
        this.reason = undefined;
        let resolve = (value) => { // 调用resolve, 变成成功态
            if (this.status === 'pending') {
                this.status= 'fulfilled';
                this.value = value;
            }  
        };
        let rejected = (reason) => { // 调用reject, 变成失败态
            if (this.status === 'pending') {
                this.status= 'rejected';
                this.reason = reason;
            }         
        };
        excutor(resolve, rejected);
    }
    then(onFulfilled, onRejected){
        console.log(this.status, 'status');
        if (this.status === 'fulfilled') { // 变成成功态fulfilled时执行成功回调
            onFulfilled(this.value);
        }
        if (this.status === 'rejected') { //  变成失败态rejected时执行失败回调
            onFulfilled(this.reason);
        }
    }
}
复制代码

实现异步promise和多个then

核心思路: 将成功和失败的回调放在数组中, 当状态改变时, 执行对应状态的事件ui

class Promise{
    constructor(excutor){
        this.status = 'pending';
        this.value = undefined;
        this.reason = undefined;
        this.onFulfilledCbs = []; // 存放成功状态的回调
        this.onRejectedCbs = []; // 存放失败状态的回调
        let resolve = (value) => {
            if (this.status === 'pending') {
                this.status= 'fulfilled';
                this.value = value;
                this.onFulfilledCbs.forEach(cb => cb()); // 依次执行成功回调
            }  
        };
        let rejected = (reason) => {
            if (this.status === 'pending') {
                this.status= 'rejected';
                this.reason = reason;
                this.onRejectedCbs.forEach(cb => cb());   // 依次执行失败回调
            }  
        };
        excutor(resolve, rejected)
    }
    then(onFulfilled, onRejected){
        ...
        if (this.status === 'pending') { // 有异步逻辑时,状态为pending 将callback放在数组中
            this.onFulfilledCbs.push(() => {
                onFulfilled(this.value);
            });
            this.onRejectedCbs.push(() => {
                onRejected(this.reason);
            });
        }
    }
}
复制代码

实现then的链式调用

思路: 返回一个新的promise实例. jquery中的方法也能链式调用, 是经过return this实现的.可是promise不能返回this, 由于, 一个promise的状态若是是fulfilled,就不能变成rejected.this

then的特色:spa

  • 不管成功仍是失败 会将成功或失败的值传递给下一级
  • 若是没有接受到返回值 会默认执行下个then的
  • 若是抛出错误,没有捕获,会报错
  • 主动抛出异常 走onRejected 此时能够将onFulfilled赋值为null
  • 主动抛出异常也能够经过catch进行捕获 而且 then()能够穿透
  • catch后能够执行then 返回值会传给下个onFulfilled
  • err捕获的原则: 先找最近的捕获,没有找下一个
class Promise{
    constructor(excutor){
        ...
        try{ // 1. 在执行excutor时有可能抛出错误 直接捕获错误
            excutor(resolve, reject);
        }catch(err) {
            reject(err);
        }
        
    }
    // resolvePromise 判断当前promise返回结果x和promise2的关系
    resolvePromise(promise2, x, resolve, reject) {
        if((typeof x === 'object' && x !== null) || typeof x === 'function'){
            try{ // 取then方法时  有可能报错 
                let then = x.then;
                if (typeof then === 'function') { // 2.2.2 返回一个promise 执行promise
                    then.call(x, (y) => {
                        resolve(y);
                    }, (r) => {
                        reject(r);
                    });
                } else { // 2.2.3 返回一个带有then属性的普通对象 {then: ...}
                    resolve(x);
                }
            }catch(e){
                reject(e);
            }
        } else {// 2.2.1 返回一个常量
            resolve(x);
        }
    }
   
    then(onFulfilled, onRejected){ 
        // 2.实现then的链式调用(jq)
        let promise2 = new Promise((resolve, reject) => { // 2.1 返回一个新的promise
            if (this.status === 'fulfilled') {
                try{ // 若是方法执行报错 直接抛出错误
                    let x = onFulfilled(this.value); // 2.2 处理前一个promise中onFulfilled的不一样返回结果和下一个promise的关系: 普通值/promise
                    this.resolvePromise(promise2, x, resolve, reject);
                }catch(e){
                    reject(e);
                }
            }
            if (this.status === 'rejected') {
                try{
                    let x = onRejected(this.reason);
                    this.resolvePromise(promise2, x, resolve, reject);
                }catch(e){
                    reject(e);
                }
            }
            if (this.status === 'pending') {
                this.onFulfilledCbs.push(() => {
                    try{
                        let x = onFulfilled(this.value);
                        this.resolvePromise(promise2, x, resolve, reject);
                    }catch(e){
                        reject(e);
                    }
                });
                this.onRejectedCbs.push(() => {
                    try{
                        let x = onRejected(this.reason);
                        this.resolvePromise(promise2, x, resolve, reject);
                    }catch(e){
                        reject(e);
                    }
                });
            }
        });
        return promise2;
    }
}
复制代码

特殊状况处理

  • 返回的promise2和当前的promise不能相同: 若是相同会致使promise2的循环调用
  • new Promise(resolve, reject)中resolve传递的值可能也是一个promise, 须要实现递归解析
  • 避免promise即调用成功又调用失败
class Promise{
    ...
    resolvePromise(promise2, x, resolve, reject) {
        if (promise2 === x) { // 4.1 return的值不能为promise2(本身等待本身) 不然会报循环引用错误
            return reject(new Error('循环引用'));
        }
        let called; // 4.4 定义called 避免成功失败只能调用一个
        if((typeof x === 'object' && x !== null) || typeof x === 'function'){
            try{
                let then = x.then;
                if (typeof then === 'function') {
                    then.call(x, (y) => {
                        if (called) return; // 成功和失败只会掉一个
                        called = true;
                        this.resolvePromise(promise2, y, resolve, reject); // 4.3 若是y是一个promise 进行递归解析
                    }, (r) => {
                        if (called) return; // 若是掉过成功
                        called = true;
                        reject(r);
                    });
                } else {
                    if (called) return;
                    called = true;
                    resolve(x);
                }
            }catch(e){
                if (called) return;
                called = true;
                reject(e);
            }
        } else {
            if (called) return;
            called = true;
            resolve(x);
        }
    }
   
    then(onFulfilled, onRejected){
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === 'fulfilled') {
                setTimeout(() => { // 4.2 若是不加setTimeout会致使promise2为undefined-->then方法都是异步的
                    try{
                        let x = onFulfilled(this.value);
                        this.resolvePromise(promise2, x, resolve, reject); // 初始化promise2未完成, 为undefined
                    }catch(e){
                        reject(e);
                    }
                });
            }
            if (this.status === 'rejected') {
                setTimeout(() => {
                    try{
                        let x = onRejected(this.reason);
                        this.resolvePromise(promise2, x, resolve, reject);
                    }catch(e){
                        reject(e);
                    }
                });
            }
            ....
        });
        return promise2;
    }
}

module.exports = Promise;
复制代码

then穿透

思路: 若是promise执行resolve, 就将resolve中的值经过onFULfilled传递; 若是promise执行reject, 就将reject中的值经过onRejected传递插件

then(onFulfilled, onRejected){
        // 5 穿透实现
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (data) => {
            return data;
        };
        onRejected = typeof onRejected === 'function' ? onRejected : (err) => {
            throw err;
        };
        ...
    }
复制代码

测试

在promise A+规范中提出, 经过promises-aplus-tests插件对本身写的promise进行检验, 可是这个插件使用前须要经过promise.defer将promise的属性挂载在defer上, 代码以下:

Promise.deferred = Promise.defer = () => {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd;
}
复制代码

下面就能够经过promises-aplus-tests插件对本身写的promise进行检验了:

  • yarn add promises-aplus-tests
  • npx promises-aplus-tests promise.js
相关文章
相关标签/搜索