Promise的简单实现

什么是Promise?

Promise 是异步编程的一种解决方案:从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

Promise的实现

Promises/A+规范原文:https://promisesaplus.com/

  1. 构建Promise类
    进行初始化,设置status为pending,data和err都为undefined,设置两个函数resolve和reject传递数据,executor默认先执行一次,具体流程看代码
 constructor(executor) {
     /* 初始化 */
     this.status = 'pending';/* 当前状态,用于控制resolve和reject的执行 */
     this.data = undefined;/* 记录成功数据 */
     this.err = undefined;/* 记录失败数据 */
     /* 成功 */
     let resolve = data => {
         if (this.status === 'pending') {
             this.data = data;
             /* 改变为成功状态后reject将不会执行 */
             this.status = 'fulfilled';
         }
     }
     /* 失败 */
     let reject = err => {
         if (this.status === 'pending') {
             this.err = err;
             this.status = 'rejected';
         }
     }
     /* 默认执行一次 */
     try {
         /* 执行executor,通过resolve/reject把data/err传给当前Promise对象 */
         executor(resolve, reject);
     } catch (e) {
         reject(e);/* 执行出错将把错误传给this.err */
     }
 }
  1. 通过获取data/err的方式传递数据给then的两个函数,实现同步情况下的then函数功能
 then(onfulfilled, onrejected) {
     /* fufilled状态说明this.data已经赋值,直接传参即可 */
     if (this.status === 'fulfilled') {
         onfulfilled(this.data);
     }
     /* rejected状态说明this.err已经赋值,直接传参即可  */
     if (this.status === 'rejected') {
         onrejected(this.err);
     }
 }
// 示例:
let p = new Promise((resolve, reject) => { reject('hello') })
p.then(
    data => { console.log(`data->${data}`) },
    err => { console.log(`err->${err}`) });
// 结果:err->hello
  1. 根据发布订阅的原理,数组存储回调函数,等待异步执行resolve/reject再触发回调函数,实现异步情况下的then函数功能
class Promise {
    constructor(executor) {
    	//省略...
        this.onfulfilled_cbs = [];/* 成功回调 */
        this.onrejected_cbs = [];/* 失败回调 */
        /* 成功 */
        let resolve = data => {
            if (this.status === 'pending') {
                this.data = data;
                /* 改变为成功状态后reject将不会执行 */
                this.status = 'fulfilled';
                /* 执行等待的回调 */
                this.onfulfilled_cbs.forEach(cb => cb());
            }
        }
        /* 失败 */
        let reject = err => {
            if (this.status === 'pending') {
                this.err = err;
                this.status = 'rejected';
                this.onrejected_cbs.forEach(cb => cb());
            }
        }
        //省略...
    }
    then(onfulfilled, onrejected) {
    	//省略代码...
        /* 当为pending态时,将回调存入对应数组,等待异步执行完再依次触发 */
        if (this.status === 'pending') {
            this.onfulfilled_cbs.push(() => {
                onfulfilled(this.data);
            })
            this.onrejected_cbs.push(() => {
                onrejected(this.err);
            })
        }
    }
}
// 示例:
let p = new Promise((resolve, reject) => {
    setTimeout(() => { reject('hello') }, 3000)
})
p.then(
    data => { console.log(`data->${data}`) },
    err => { console.log(`err->${err}`) });
// 结果:err->hello 
  1. 实现then函数的链式调用
    (1)实现每个then函数执行后返回一个新的Promise对象nextPromise,只有这样才能不断产生和执行新的then函数
    (2)记录当前then里面两个回调的返回值,返回值即可能是普通值也可能是Promise对象,需要将其处理成为普通值再传给nextPromise
    a.当then两个回调为空或者不是函数时,设置默认函数
    b.为了解决对象未创建完成前无法对其进行赋值的问题,我们需要将处理的过程放到一个异步api里面,采用异步方式对nextPromise赋值
 then(onfulfilled, onrejected) {
     /* 设置默认 */
     onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data;
     onrejected = typeof onrejected === 'function' ? onrejected : err => { throw err };
     /* 创建新对象 */
     let nextPromise = new Promise1((resolve, reject) => {
         /* fufilled状态说明this.data已经赋值,直接传参即可 */
         if (this.status === 'fulfilled') {
             setTimeout(() => {
                 try {
                     /* 记录返回值 */
                     let res = onfulfilled(this.data);
                     /* 对返回值处理后传入新的对象 */
                     resolvePromise(nextPromise, res, resolve, reject);
                 } catch (e) {
                     /* 如果出错直接捕获传给nextPromise */
                     reject(e);
                 }
             })
         }
         /* rejected状态说明this.err已经赋值,直接传参即可  */
         if (this.status === 'rejected') {
             setTimeout(() => { /*省略...*/ })
         }
         /* 当为pending态时,将回调存入对应数组,等待异步执行完再依次触发 */
         if (this.status === 'pending') {
             this.onfulfilled_cbs.push(() => {
                 setTimeout(() => { /*省略...*/ })
             })
             this.onrejected_cbs.push(() => {
                 setTimeout(() => { /*省略...*/ })
             })
         }
     })
     return nextPromise;
 }

(3)对res的类型进行检测并处理,对Promise向下递归直到res为普通类型后传递给nextPromise,设置called防止测试出现错误。

let resolvePromise = (nextPromise, res, resolve, reject) => {
    /* 根据Promise/A+上的要求,如果返回值和新对象是同一个,直接报错 */
    if (nextPromise === res) {
        return reject(new TypeError(new TypeError('Chaining cycle detected for promise #<Promise>')))
    }
    /* 判断是对象或者函数 */
    if (typeof res === 'object' && res !== null || typeof res === 'function') {
        let called = false;//测试时 可能用两个版本的Promise 设置以防止防止失败后调用成功 
        /* 文档要求防止取then出错 */
        try {
            /* 获取res.then进一步判断 */
            let then = res.then;
            /* then是函数说明res是Promise对象 */
            if (typeof then === 'function') {
                /* 执行then */
                then.call(res, data => {
                    if (called) return;
                    called = true;
                    /* 递归直到非Promise对象再传给resolve */
                    resolvePromise(nextPromise, data, resolve, reject);
                }, err => {/* 继续传递错误 */
                    if (called) return;
                    called = true;
                    reject(err);
                })
            } else {/* 普通对象直接传值 */
                if (called) return;
                called = true;
                resolve(res);
            }
        } catch (e) {/* 运行错误直接给reject */
            if (called) return;
            called = true;
            reject(e);
        }
    } else { resolve(res); }/* 普通类型直接返回 */
}
// 示例:
let p = new Promise((resolve, reject) => {
    resolve('hello')
})
let p2 = p.then(data => {
    console.log(`data->${data}`);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('999');
        })
    })
}, err => console.log(`err->${err}`))
p2.then(data => console.log(`data2->${data}`),
    err => console.log(`err2->${err}`))
// 结果:data->hello   err2->999
  1. 使用promises-aplus-tests库并进行测试
    安装:npm install promises-aplus-tests -g
    运行:promises-aplus-tests 当前js文件路径
Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}
module.exports = Promise;
  1. 实现Promise.all(),遍历传入的任务数组,若为Promise对象,则执行then函数存返回值,若为普通值,直接存入要传回的数组。
function isPromise(p) {
    if (typeof p === 'object' && p !== null || typeof p === 'function') {
        if (typeof p.then === 'function') {
            return true;
        }
    }
    return false;
}
Promise.all = (tasks) => {
    return new Promise((resolve, reject) => {
        let results = [], index = 0;
        function processData(key, value) {
            results[key] = value;
            /* index计数,在异步操作时key小的会在后面执行 */
            if (++index === tasks.length) {
                resolve(results);
            }
        }
        for (let i = 0; i < tasks.length; i++) {
            let task = tasks[i];
            if (isPromise(task)) {
                task.then(data => processData(i, data));
            } else {
                processData(i, task);
            }
        }
    })
}
//示例:
function say(str) {
    let dfd = Promise.defer();
    setTimeout(() => dfd.resolve(str));
    return dfd.promise;
}
Promise.all([1, 2, 3, say('hello'), 5, 6, 7, say('bye')])
    .then(data => console.log(data),
        err => console.log(err))
// [ 1, 2, 3, 'hello', 5, 6, 7, 'bye' ]
  1. 其他功能有时间再写…
posted @ 2020-04-11 23:40  aeipyuan  阅读(220)  评论(0编辑  收藏  举报