简单实现 promise 笔记

152 阅读7分钟

Promise 简单理解:

promise 是对某人某事做许诺, 无论多久无论多远, 这个承诺肯定会实现, 或许会是成功, 或许会是失败, 但肯定会得到结果.

Promise 有三种状态

  • pending 初始状态

  • fullfilled 成功后的状态

  • rejected 失败的状态

!! 状态转变规则: 只能由 pending 变为 fullfilled 或 rejected, 且在状态改变后不能再次改变.

Promise 是种异步流程的控制手段

Promise 可以解决类似 ajax 的异步回调地狱问题, Promise 采用了链式调用来解决上述问题, 且支持多个并发请求, Promise 的 then 方法是异步的, 但 Promise 本身不是异步.

promise.then().then().then()

1, 简单理解三种状态

  • 只有 pending 状态时可以改变状态; 状态发生改变后不可再次改变; Promise 实例上有个 then 方法, 可以接收两个参数(成功和失败函数); Promise 在发生错误时会直接执行失败函数, 并变为失败状态;
const primise = new Promise((resolve, reject) => { // 默认 promise 中的excutor是同步执行的。
    resolve('成功');
    reject('失败');
}

promise.then(data => {
    console.log('success');
}, err => {
    console.log('failed');
}));

2, promise 链式调用解决 ajax 回调地狱

ajax 回调 demo:
```js
// ajax 1次
$.ajax(
    ...,
    success() {
        // ajax 2次
        $.ajax(
            ...,
            success() {
                ......
                // ajax n 次
            }
        )
    }
)
```

Promise 链式调用:

  • 如果 promise 返回的是 Promise 对象, 则成功和失败函数中会获取这个 Promise 的结果, 如果是成功状态就会执行外层 promise 的 resolve 方法, 并将数据传入该函数, 反之会传入 reject 函数.

  • promise 实现链式调用返回的不是 this 对象, 而是新的 Promise 对象.

$fn(param, callback)

function ajax(param){
    return new Promise((resolve, reject) => {
        $fn(param, function(err, data){
            if(err) reject(err);
            resolve(data);
        }
    }
}

ajax(param).then(data => {
    return ajax(data); // 返回新的 Promise 对象
    /* return 200; // 返回了普通的值, 会走下个 then 中的 resolve 方法
        throw new Error(); // 发生错误时会走下个 then 中的 reject 方法. 
    */
}).then(data => {
    return ajax(data);
}, err => {
    console.log(err);
}).then(data => {
    return ajax(data);
}).then(data => {
    return ajax(data);
}).catch(err){
    // 也可以集中处理错误;
};
// 即使 promise 中有失败, 也可以继续 then.

3, Promise 的其他常用方法:

① Promise.all 处理并发的请求, 并在全都处理成功时变为 fullfilled 状态, 如果其中有发生错误,则状态立即变为 reject 状态.

$fn(param, callback)

function ajax(param){
    return new Promise((resolve, reject) => {
        $fn(param, function(err, data){
            if(err) reject(err);
            resolve(data);
        }
    }
}

Promise.all([ajax('1'), ajax('2')]).then((r1,r2)=>{
  console.log(r1,r2);
},err=>{
  console.log(err);
});

② Promise.race 处理多个并发请求, 执行顺序不确定, 但只在最快的请求完成时改变状态.

$fn(param, callback)

function ajax(param){
    return new Promise((resolve, reject) => {
        $fn(param, function(err, data){
            if(err) reject(err);
            resolve(data);
        }
    }
}

Promise.race([ajax('1'), ajax('2')]).then((r1,r2)=>{
  console.log(r1,r2);
},err=>{
  console.log(err);
});

③ Promise.resolve() 返回成功状态的 Promise Promise.reject() 返回失败状态的 Promise

Promise.resolve('success').then(data => {
    console.log('data') //success
});

Promise.reject('fail').then(data => {
    console.log(data); //fail
});

手动实现 Promise

promisify

// 基础版本
class Promise{
    // 构造器
    constructor(executor) {
        // 初始化状态
        this.state = 'pending';
        // 成功的值
        this.value = undefined;
        // 失败的原因
        this.reason = undefined;

        // 成功
        const resolve = value => {
            if (this.state === 'pending') {
                // 执行成功后改变状态
                this.state = 'fullfilled';
                // 保存成功的值
                this.value = value;
            }
        };
        // 失败
        const reject = reason => {
            if (this.state === 'pending') {
                // 执行失败后改变状态
                this.state = 'rejected';
                // 保存失败的原因
                this.reason = reason;
            }
        };
        // 立即执行, 如果有错误直接执行 reject
        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err);
        }
        
    }
};
// 完善 then 方法
class Promise{
    // 构造器
    constructor(executor) {
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;

        const resolve = value => {
            if (this.state === 'pending') {
                this.state = 'fullfilled';
                this.value = value;
            }
        };
        const reject = reason => {
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = reason;
            }
        };
        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err);
        }   
    }
    // then 方法 有两个参数(onFulfilled onRejected)
    then(onFulfilled, onRejected) {
        // 状态为fulfilled,执行onFulfilled,传入成功的值
        if (this.state === 'fulfilled') {
            onFulfilled(this.value);
        } else if (this.state === 'rejected') {
            // 状态为rejected,执行onRejected,传入失败的原因
            onRejected(this.reason);
        };
    }
};
// 完善异步方法
class Promise{
    constructor(executor) {
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;

        // 成功存储的数组
        this.onResolvedCallbacks = [];
        // 失败存储的数组
        this.onRejectedCallbacks = [];
        
        const resolve = value => {
            if (this.state === 'pending') {
                this.state = 'fullfilled';
                this.value = value;

                // resolve执行,调用成功数组的函数
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        };
        const reject = reason => {
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = reason;

                // resolve执行,调用成功数组的函数
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        };
        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err);
        }   
    }
    then(onFulfilled, onRejected) {
        if (this.state === 'fulfilled') {
            onFulfilled(this.value);
        } else if (this.state === 'rejected') {
            onRejected(this.reason);
        };

        // 当状态state为pending时
        if (this.state === 'pending') {
            // onFulfilled传入到成功数组
            this.onResolvedCallbacks.push(() => {
                onFulfilled(this.value);
            });
            // onRejected传入到失败数组
            this.onRejectedCallbacks.push(() => {
                onRejected(this.value);
            });
        }
    }
};
// 完善链式调用
class Promise{
    constructor(executor) {
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;

        // 成功存储的数组
        this.onResolvedCallbacks = [];
        // 失败存储的数组
        this.onRejectedCallbacks = [];
        
        const resolve = value => {
            if (this.state === 'pending') {
                this.state = 'fullfilled';
                this.value = value;

                // resolve执行,调用成功数组的函数
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        };
        const reject = reason => {
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = reason;

                // resolve执行,调用成功数组的函数
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        };
        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err);
        }   
    }
    then(onFulfilled, onRejected) {
        // 返回新的 Promise 对象
        const newPromise =  new Promise((resolve, reject) => {
            if (this.state === 'fulfilled') {
                const fullfilled = onFulfilled(this.value);

                // Promise A+ 规范规定: 让不同的promise代码互相套用,叫做resolvePromise
                resolvePromise(newPromise, fullfilled, resolve, reject);
            } else if (this.state === 'rejected') {
                onRejected(this.reason);
            };

            // 当状态state为pending时
            if (this.state === 'pending') {
                // onFulfilled传入到成功数组
                this.onResolvedCallbacks.push(() => {
                    const fullfilled = onFulfilled(this.value);
    
                    // Promise A+ 规范规定: 让不同的promise代码互相套用,叫做resolvePromise
                    resolvePromise(newPromise, fullfilled, resolve, reject);
                });
                // onRejected传入到失败数组
                this.onRejectedCallbacks.push(() => {
                    const rejected = onRejected(this.value);

                    // Promise A+ 规范规定: 让不同的promise代码互相套用,叫做resolvePromise
                    resolvePromise(newPromise, rejected, resolve, reject);
                });
            }
        });
        return newPromise;
    }
};
/* 完整的 Promise */
// 1. 实现 Promise 类
class Promise {
    constructor(executor) {
        this.status = 'pending';
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        const resolve = (data) => {
            if (this.status === 'pending') {
                this.status = 'resolved';
                this.value = data;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        };

        const reject = (data) => {
            if (this.status === 'pending') {
                this.status = 'rejected';
                this.reason = data;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        };

        try {
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }

    then(onFulfilled, onRejected) {
        // onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : y => y;
        // onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
        if (typeof onFulfilled !== 'function') {
            y => y;
        }
        if (typeof onRejected !== 'function') {
            err => { throw err };
        }

        let newPromise;

        if (this.status === 'pending') {
            newPromise = new Promise((resolve, reject) => {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            const fullfilled = onFulFilled(this.value);
                            resolvePromise(newPromise, fullfilled, resolve, reject);
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);
                });
                // 存储失败回调
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let rejected = onRejected(this.reason);
                            resolvePromise(newPromise, rejected, resolve, reject);
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);
                });
            });
        }
        if (this.status === 'resolved') {
            newPromise = new Promise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        const fullfilled = onFulFilled(this.value);
                        resolvePromise(newPromise, fullfilled, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0);
            });
        }
        if (this.status === 'rejected') {
            newPromise = new Promise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        let rejected = onRejected(this.reason);
                        resolvePromise(newPromise, rejected, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0);
            });
        }
        // 调用 then 方法返回新的 Promise
        return newPromise;
    }
    // catch Promise 中的错误信息
    catch(onRejected) {
        // catch 就是 then 失败时的简写
        return this.then(null, onRejected);
    }
}

// 2, 官方给出的 resolvePromise 函数, 用来验证 Promise 类
/* 
* 如果 then 是 function, 调用 x 作为传入的 this, 第二个参数 resolvePromise 为:
*     如果/当 resolvePromise 被值 y 调用时, 就执行 [[Resolve]](promise, y);
*     如果/当 rejectPromise 被 r 作为拒绝原因调用时, 就执行 reject 并 抛出异常 r;
*     如果/当 resolvePromise 和 rejectPromise 都被调用, 或相同的参数导致多次调用时, 只有第1个调用生效, 其他调用将被忽略
* 如果调用 then 抛出异常了,
*     如果调用了 resolvePromise 或 rejectPromise, 调用将被忽略;
*     否则 promise 状态变为 reject, 并抛出异常信息 e
*/
function resolvePromise(newPromise, x, resolve, reject) {
    if (newPromise === x) {
        return reject(new TypeError('循环调用'));
    }
    
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        let called;
        try {
            // 防止取 then 时出现异常 Object.defineProperty
            const { then } = x; // 取x的then方法 {then:{}}
            if (then === 'function') { // 如果then是函数就认为这是promise
                // call 的第1个参数是this ,后面的参数是成功的回调和失败的回调
                then.call(x, y => { // 成功的回调
                    // 如果y是promise就继续递归解析promise
                    if (called) return;
                    called = true;
                    resolvePromise(newPromise, y, resolve, reject);
                }, r => { // 失败的回调
                    if (called) return;
                    called = true;
                    reject(r);
                });

            } else {
                // then 是普通值
                resolve(x);
            }

        } catch (e) {
            if (called) return;
            called = true;
            reject(e);
        }
    } else {
        resolve(x); // x 就是个普通值
    }
}

// 3, Promise的常用方法
// 生成成功的promise
Promise.resolve = (value) => {
    return new Promise((resolve, reject) => resolve(value));
}

// 生成失败的promise
Promise.reject = (reason) => {
    return new Promise((resolve, reject) => reject(reason));
}

// Promise.race 方法, 返回多个 promise 中结果最快的那个, 无论本身的状态是成功还是失败
Promise.race = (promises) => {
    return new Promise((resolve, reject) => {
        const length = promises.length;
        for (let i = 0; i < length; i++) {
            promises[i].then(resolve, reject);
        }
    })
}

// Promise.all,该方法的作用是将多个 Promise 对象实例包装,生成并返回1个新的 Promise 实例。
Promise.all = (promises) => {
    return new Promise((resolve, reject) => {
        const length = promises.length;
        const arr = [];
        let i = 0; // 为了保证获取到全部的成功给每个 Promise 加上个索引
        function process(index, data) {
            arr[index] = data;
            i++;
            if (i === length) {
                resolve(arr);
            }
        }
        for (; i < length; i++) {
            promises[i].then(resolve => {
                process(i, resolve);
            }, reject);
        }
    });
}

// Promise.deferred, 异步模式
Promise.deferred = function () {
    const deferred = {};
    deferred.promise = new Promise((resolve, reject) => {
        deferred.resolve = resolve;
        deferred.reject = reject;
    })
    return deferred;
}

// 4, 导出模块
module.exports = Promise;

// 5, 测试代码, 安装模块:npm install promises-aplus-tests -g 执行命令:promises-aplus-tests 文件名,即可得到结果

参考来源: BAT前端经典面试问题:史上最最最详细的手写Promise教程