手写Promise

94 阅读6分钟

Promise

1. 介绍与基本使用

1.1 Promise是什么?

抽象表达:

  1. Promise是一个新的ES6规范
  2. Promise是JS中异步编程的 新解决方案 注:旧方案单纯使用回调函数

具象表达:

  1. 从语法上说,Promise是一个构造函数,可以进行对象的实例化
  2. 从功能上说:Promise对象用来封装一个异步操作,并取到成功/失败的结果值

异步操作列举(包括但不限于):
1)fs 文件操作 2)数据库操作 3)AJAX 4)定时器

1.2 为什么要用Promise?

1.2.1 指定回调函数的方式更加灵活

  1. 旧的,必须在启动任务前指定,例如:
setTimeout(() => {
  // 回调函数
}, 2000);

  1. Promise:可以在启动异步任务之后再绑定回调函数,甚至可以绑定多个
const p = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    resolve('ok');
  }, 2000);
});

p.then((value) => {
  // 执行成功的回调函数
  console.log(value); // ok
});

p.then((value) => {
  // 可以多次调用
  console.log(123); // 123
});

1.2.2 支持链式调用,解决回调地狱问题(重要)

  1. 什么是回调地狱?看代码
asyncFunc1(opt, (...arg1) => {
  asyncFunc2(opt, (...arg2) => {
    asyncFunc3(opt, (...arg3) => {
        // 执行操作
    });
  });
});
  1. 回调地狱缺点:

不便于阅读,不便于异常处理

1.3 Promise的基本用法

const promise = new Promise((resolve, reject) => {
    // do someing
    if (/* 异步操作成功 */) {
        resolve(value);
    } else {
        reject(error);
    }
});
promise.then(
// 成功回调
(value) => {
  console.log(`success ${value}`)
},
// 失败回调
(reason) => {
  console.log(`fail ${reason}`)
})

1.4 Promise的状态改变

1.4.1 Promise的状态

实例对象中的一个属性 「PromiseState」

  1. pending 变为 resolved(同fulfilled)
  2. pending 变为 rejected

默认起始状态为pending(未决定的),改变后的状态只有这两种fulfilled(已成功)和rejected(已失败),并且一个Promise对象状态只能改变一次,成功或者失败,成功的结果数据一般称为value,失败的结果数据一般为reason

1.4.2 Promise的值

实例对象中的另一个属性「PromiseResult」

保存着「成功/失败」的结果值,是由resolvereject两个函数的返回值决定的

2. API

2.1 构造函数:Promise(executor){}

  1. executor函数:执行器(resolve, reject) => {}
  2. resolve函数:内部定义成功时调用的函数(value) => {}
  3. reject函数:内部定义失败时调用的函数(reason) => {}

executor 会在内部立即同步调用

2.2 Promise.prototype.then方法:(onResolved, onRejected) => {}

  1. onResolved函数:成功时回调函数(value) => {}
  2. onRejected函数:失败时回调函数(reason) => {}

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

2.3 Promise.prototype.catch方法:(onRejected) => {}

  1. onRejected函数:失败时回调函数(reason) => {}

2.4 Promise.resolve方法:(value) => {}

  1. value:成功的数据或Promise对象
  2. 返回值为成功/失败的Promise对象

2.5 Promise.reject方法:(reason) => {}

  1. reason:失败的原因
  2. 返回值为失败的Promise对象

2.6 Promise.all方法:(promiseArr) => {}

  1. promiseArr:包含n个Promise对象的数组
  2. 返回值一个新Promise对象,所有的Promise成功才成功,只要有一个失败就直接失败

2.7 Promise.race:(promiseArr) => {}

  1. promiseArr:包含n个Promise对象的数组
  2. 返回值一个新Promise对象,第一个完成的Promise的结果状态就是最终的结果状态

3. 关键问题

3.1 如何改变Promise的状态?

  1. resolve(value): pending => resolved
  2. reject(reason): pending => rejected
  3. throw抛出异常: pending => rejected

3.2 指定多个成功或失败的回调函数,都会执行吗?

当状态改变时,同一个Promise的多个then/catch函数都会执行,参考1.2.1

3.3 改变Promise状态和指定回调函数谁先谁后?

都有可能

其实是同步异步的问题,执行器是异步函数就是先指定回调函数,再改变状态,反之同步函数则是先改变状态,再指定回调函数

3.4 Promise.then()返回的Promise状态由什么决定?

简单来说,由then指定的回调函数的执行结果决定

const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('ok');
    });
});

const result = p.then((value) => {
  // 1. 抛出错误
  // throw 'Error'
  // result为{PromiseState: 'rejected', PromiseResult: 'error'}
  
  // 2. 返回结果为非Promise类型的对象
  // return 'success'; 
  // result为{PromiseState: 'fulfilled', PromiseResult: 'success'}
  
  // 3. 返回结果是Promise对象
  // return new Promise((resolve) => { resolve('123')})
  // result为新的Promise的结果{PromiseState: 'fulfilled', PromiseResult: '123'}
});

3.5 Promise如何串联多个操作任务?

通过then()链式调用

const p = new Promise((resolve, reject) => {
  resolve('ok');
}).then((value) => {
  return value;
}).then((v2) => {
  return v2;
});

3.6 Promise异常穿透?

当使用then链式调用时,只需在最后指定一次失败的回调,前面任何操作出了异常都会在传到失败的回调函数中处理

const p = new Promise((resolve, reject) => {
  resolve('ok');
}).then((value) => {
  throw 'error';
}).then((v2) => {
  throw 'no';
}).catch((reason) => {
    console.log(reason) // error
});

3.7 中断Promise链?

当使用链式调用时,在中间间断,不再调用后面的回调函数

方法:在回调函数中返回一个pending状态的Promise对象

const p = new Promise((resolve, reject) => {
  resolve('ok');
}).then((value) => {
  console.log(value);
  // 中断,有且只有一种方式
  return new Promise(() => {});
}).then((v2) => {
  console.log(v2); 
}).catch((reason) => {
    console.log(reason)
});

4. 自定义封装

4.1 声明构造函数

// 声明构造函数
function Promise(executor) {

    // 添加 属性
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    // 回调函数
    this.callbacks = [];

    // 保存实例对象的this值
    const self = this;

    try {
        // 同步调用「执行器函数」
        executor(resolve, reject);
    } catch (e) {
        // 修改Promise的对象状态为「失败」
        reject(e);
    }

    // 成功回调
    function resolve(data) {
        common('fulfilled', data);
        // 回调函数异步执行
        setTimeout(() => {
            // 执行then中的回调
            self.callbacks.forEach(({onResolved}) => {
                onResolved(data);
            });
        });
    }
    // 失败回调
    function reject(data) {
        common('rejected', data);
        // 回调函数异步执行
        setTimeout(() => {
            // 执行then中的回调
            self.callbacks.forEach(({onRejected}) => {
                onRejected(data);
            });
        });
    }

    function common(state, data) {
        // 状态只能改变一次
        if (self.PromiseState !== 'pending') return;
        // 改变状态和值
        self.PromiseState = state;
        self.PromiseResult = data;
    }
}

4.2 添加 then 方法

// 添加 then 方法
Promise.prototype.then = function(onResolved, onRejected) {
    const self = this;
    // then方法返回值为Promise对象
    return new Promise((resolve, reject) => {
        // 封装函数
        function callback(type) {
            try {
                // 获取回调函数的执行结果
                const result = type(self.PromiseResult);
                // 执行结果是Promise对象,执行then方法
                if (result instanceof Promise) {
                    result.then((v) => {
                        resolve(v);
                    }, (r) => {
                        reject(r);
                    });
                } else {
                    // 执行结果的对象状态为「成功」
                    resolve(result);
                }
            } catch (e) {
                reject(e);
            }
        }
        // 根据状态调用回调函数
        if (this.PromiseState === 'fulfilled') {
            // 回调函数异步执行
            setTimeout(() => {
                callback(onResolved);
            });
        }
        if (this.PromiseState === 'rejected') {
            // 回调函数异步执行
            setTimeout(() => {
                callback(onRejected);
            });
        }
        // 异步操作
        if (this.PromiseState === 'pending') {
            // 保存回调函数
            this.callbacks.push({
                onResolved: function() {
                    callback(onResolved);
                },
                onRejected: function() {
                    callback(onRejected);
                },
            })
        }
    })
}

4.2 添加 catch 方法

// 添加 catch 方法
Promise.prototype.catch = function(onRejected) {
    return this.then(undefined, onRejected);
}

4.2 添加 resolve 方法

// 添加 resolve 方法
Promise.resolve = function(value) {
    // 返回Promise对象
    return new Promise((resolve, reject) => {
        if (value instanceof Promise) {
            value.then((v) => {
                resolve(v);
            }, (r) => {
                reject(r);
            });
        } else {
            // 执行结果的对象状态为「成功」
            resolve(value);
        }
    })
}

4.2 添加 reject 方法

// 添加 reject 方法
Promise.reject = function(reason) {
    // 返回Promise对象
    return new Promise((resolve, reject) => {
        reject(reason);
    })
}

4.2 添加 all 方法

// 添加 all 方法
Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        const res = [];
        let count = 0;
        // 虽然是循环,但状态只改变一次
        for (let i = 0, len = promises.length; i < len; i ++) {
            const p = promises[i];
            p.then((v) => {
                count++;
                // 将结果保存
                // res.push(v); // 因为执行顺序可能不一致,用push会导致下标对应不一致
                res[i] = v;
                // 改变状态
                if (count === len) {
                    resolve(res);
                }
            }, (r) => {
                reject(r);
            });
        }
    });
}

4.2 添加 race 方法

// 添加 race 方法
Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        for (let i = 0, len = promises.length; i < len; i ++) {
            const p = promises[i];
            p.then((v) => {
                resolve(v);
            }, (r) => {
                reject(r);
            });
        }
    });
}