async的实现分析

async/await 就是 Generator 的语法糖,使得异步操作变得更加方便。其中Generator的*换成了async关键字, yield换成了await关键字。

Async函数与Generator相比,甜在哪里?

  • async函数内置执行器,函数调用之后会自动执行,输出最后的结果。而Generator需要配合next和co模块一起使用。
  • 更好的语义, async表示函数里有异步操作,而await表示紧跟在后面的表达式需要等待结果。
  • 更广的适用性,co模块约定, yield后面只能是thunk函数或者Promise对象,但是async可以是Promise对象和原始类型的值 。
  • async的返回值是Promise对象,而Generator返回值是iterator。

Async的实现原理,就是将Generator和自动执行函数包装在函数里。

下面是看的博客的分析实现,自己理解分析一下

    function my_co(it) {
        return new Promise( (resolve, reject) => {
            function next(data) {
                try{
                    let {value, done} = it.next(data);

                }catch(err) {
                    reject(err);
                }

                if(!done) {
                    Promise.resolve(value).then( val => next(val), reject);
                }else{
                    resolve(value);
                }
            }
            next();
        });
    }

实际运行一下

    function* test() {
            yield new Promise( (resolve, reject) => {
                setTimeout(resolve(10), 100);
            });

            yield new Promise( (resolve, reject) => {
                resolve(100);
            });

            yield 10;

            return 1000;
        }

    my_co(test()).then(val => console.log(val) ).catch(err => console.log(err)) //output 1000

下面是执行过程分析

    function my_co(it) {
            return new Promise( (resolve, reject) => {
                function next(data) { //第一次调用,data的值为undefined,第二次调用data为promsie[10],第三次掉用为romise[100],第四次调用为10
                    try {
                        var {value, done} = it.next(data); // {value:promise obj, done:false}, data=undefined;{value:promise obj, done:false}, data=10; {value:10, done:false}, data=100;{value:undefined, done:true}, data=10
                    } catch (error) {
                        reject(error);
                    }

                    if(!done) { //true
                        //有可能值时vlaue或者promise对象
                        Promise.resolve(value).then((val) => { //第一次value:promise, 第二次value:promise,第三次为10
                            console.log(value)
                            next(val);
                        }, reject);
                    }else{
                        console.log(value);
                        resolve(value); //第四次调用 value为1000,决议完成(上面的test中若是没有返回值或者返回值为空,则value为undefined)
                    }
                }

                next(); //执行一次
            });
        }

更新补充

Promise.race实现

> Pormsie.race特点分析 + Promise.race返回的仍然是一个Promise. 它的状态与第一个完成的Promise的状态相同。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个Promise是哪一种状态。 + 如果传入的参数是不可迭代的,那么将会抛出错误。 + 如果传的参数数组是空,那么返回的 promise 将永远等待。 + 如果迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,则 Promise.race 将解析为迭代中找到的第一个值
    Promise.race = function (promises) {
        return new Promise( (resolve, reject) => {
            if(promises[Symbol.iterator] != 'function') {
                console.log('args must be iteratable');
            }

            if(promises.length == 0) {
                return;
            }else{
                for(let promise of promises) {
                    Promise.resolve(promise).then( (val) => {resolve(val); return;},
                    err => {reject(err); return;} );
                }
            }
        })
    }

    //tests
    Promise.race([]).then( (data) => console.log('success', data) , err=> console.log('error', err));
    Promise.race().then( (data) => console.log('success', data) , err=> console.log('error', err));
    Promise.race([
        new Promise((resolve, reject) => setTimeout(resolve(100), 100)),
        new Promise((resolve, reject) => setTimeout(resolve(200), 100)),
        new Promise((resolve, reject) => setTimeout(resolve(300), 100))
    ]).then( (data) => console.log('success', data) , err=> console.log('error', err));

可遍历数据结构有什么特点

一个对象如果要具备可被 for...of 循环调用的 Iterator 接口,就必须在其 Symbol.iterator 的属性上部署遍历器生成方法(或者原型链上的对象具有该方法)

    let obj = {
        name: 'eng',
        age: 18,
        job: "engineer"
        [Symbol.iterator]() {
            const self = this;
            const key = Object.keys(self); //Object.keys method返回一个自身属性名的数组,顺序是按照正常的遍历顺序
            let index = 0;
            return {
                next() {
                    if(index < keys.length) {
                        return {
                            value: self[key[index++]],
                            done: false;
                        }
                    }else {
                        return {
                            value: undefined,
                            done: true
                        }
                    }
                }
            }
        }
    }

    for(let i of obj) {
        console.log(i);
    }

    //上面的方法可以用Generator进行重写
    let obj = {
        name: "eng",
        age: 18,
        job: "engineer",
        *[Symbol.iterator]() {
            let self = this;
            let key = Object.keys(self);
            for(let index = 0; index < key.length; index++) {
                yield self[key[index]];            
            }
        }
    }

    for(let i of obj) {
        console.log(i);
    }

    /**
     * browerlist: {
     *  "IE": ">=9" 
     * }
     * 
     * 为了兼容,我们需要pollyfill
     * */
    if(!Object.keys) {
        Object.keys = (function() {
            'ues strict';
            var hasOwnProperty = Object.prototype.hasOwnProperty,
                hasDontEnumBug = !({ toString: null}).propertyIsEnumerable('toString'),
                dontEnums = [
                    'toString',
                    'toLocaleString',
                    'valueOf',
                    'hasOwnProperty',
                    'isPrototypeOf',
                    'propertyIsEnumerable',
                    'constructor'
                ],
                dontEnumLength = dontEnums.length;

            return function(obj) {
                if (typeof obj !== 'function' && (typeof obj !== 'object' || obj === null)) {
                    throw new TypeError('Object.keys called on non-object');
                }

                var result = [], prop, i;

                for (prop in obj) {
                    if (hasOwnProperty.call(obj, prop)) {
                    result.push(prop);
                    }
                }

                if (hasDontEnumBug) {
                    for (i = 0; i < dontEnumsLength; i++) {
                    if (hasOwnProperty.call(obj, dontEnums[i])) {
                        result.push(dontEnums[i]);
                    }
                    }
                }
                return result;
            };
        })()
    }

原生具备Iterator接口的对象如下

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的Arguments对象
  • NodeList对象
  • ES6的数组 、Set、Map都有entries()、values()、keys()都返回可迭代对象。

实现双向绑定 Proxy 与 Object.defineProperty 相比优劣如何?

  • Object.definedProperty 的作用是劫持一个对象的属性,劫持属性的getter和setter方法,在对象的属性发生变化时进行特定的操作。而 Proxy 劫持的是整个对象。
  • Proxy 会返回一个代理对象,我们只需要操作新对象即可,而 Object.defineProperty 只能遍历对象属性直接修改
  • Object.definedProperty 不支持数组,更准确的说是不支持数组的各种API,因为如果仅仅考虑arry[i] = value 这种情况,是可以劫持的,但是这种劫持意义不大。而 Proxy 可以支持数组的各种API。
  • 尽管Object.defineProperty有很多缺陷,但是兼容性是比Proxy好的

ps: Vue2.x 使用 Object.defineProperty 实现数据双向绑定,V3.0 则使用了 Proxy.

    //拦截器
    let obj = {};
    let temp = 'ai';
    Object.defineProperty(obj, 'name', {
        get() {
            console.log('get success');
            return temp;
        },

        set(value) {
            console.log('set success');
            temp = value;
        }
    })

    obj.name = "chris";
    console.log(obj.name);

ps: Object.defineProperty 定义出来的属性,默认是不可枚举,不可更改,不可配置【无法delete】

我们可以看到 Proxy 会劫持整个对象,读取对象中的属性或者是修改属性值,那么就会被劫持。但是有点需要注意,复杂数据类型,监控的是引用地址,而不是值,如果引用地址没有改变,那么不会触发set。

    let obj = {name:'ai', habbits: ['travel', 'swimming'], info: {
        age:10,
        job: 'engineer'
    }}

    let p = new Proxy(obj, {
        get(target, key){
           console.log('get success');
           return Reflect.get(targte, key) 
        },

        set(target, key, value) {
            console.log('set success');
            Reflect.set(target, key, value);
        }
    });

    p.name = 20; //设置成功
    p.age = 20; //设置成功; 不需要事先定义此属性
    p.hobbits.push('photography'); //读取成功;注意不会触发设置成功
    p.info.age = 18; //读取成功;不会触发设置成功

我们再看下对于数组的劫持,Object.definedProperty 和 Proxy 的差别。Object.definedProperty 可以将数组的索引作为属性进行劫持,但是仅支持直接对arry[i]进行操作,不支持数组的API,非常鸡肋。

    let array = [];

    Object.defineProperty(array, '0', {
        get() {
            console.log('get success');
            return temp;
        },

        set(value) {
            console.log('set success');
            temp = value;
        }
    });

    array[0] = 10;
    array.push(11); 

Proxy 可以监听到数组的变化,支持各种API。注意数组的变化触发get和set可能不止一次,如有需要,自行根据key值决定是否要进行处理

    let array = ['travel', 'swim'];
    let p = new Proxy(array, {
        get(target, key){
           console.log('get success');
           return Reflect.get(target, key);
        },

        set(target, key, value) {
            console.log('set success');
            return Reflect.set(target, key, value);
        }
    });

    p.push('running'); //触发get 和 set
    p.pop();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值