zoukankan      html  css  js  c++  java
  • 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. 其他功能有时间再写…
  • 相关阅读:
    组件间通信
    Android抓包方法(一)之Fiddler代理
    汉字转拼音
    post请求参数问题
    post请求参数设置
    swagger
    IfcPresentationDefinitionResource(介绍定义资源)
    Java计算两个日期之间的时间差(毫秒数)
    IfcPresentationAppearanceResource(外观定义资源)
    IfcMeasureResource(度量资源)
  • 原文地址:https://www.cnblogs.com/aeipyuan/p/12726198.html
Copyright © 2011-2022 走看看