最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 函数式编程与JS异步编程、手写Promise作业

    正文概述 掘金(冰山777)   2021-01-09   449

    简答题

    一、谈谈你是如何理解JS异步编程,EventLoop、消息队列都是做什么的,什么是宏任务,什么是微任务?

    1.JS异步编程

    要理解JS的异步编程,首先要知道JavaScript是单线程执行机制, 由于单线程的执行机制,意味着没有线程协同处理任务,那么势必在一堆同步任务队列下,面对一些耗时的任务时, 会发生阻塞,导致处理效率不佳。

    JS异步编程的首要任务就是来提高JS单线程执行机制下的处理效率的,针对一些耗时操作及宿主环境下的api交互, 例如接口的请求调用,文件的读写操作,消息发送及接收,通过异步编程的方式,都能得到很好的提升。

    常用的JS异步编程的处理方式有回调函数、事件、Promise、Generator、Async Await

    2. EventLoop、消息队列

    Wikipedia这样定义EventLoop:Event Loop是一个程序结构,用于等待和发送消息和事件。简单说,就是在程序中设置两个线程:一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event Loop线程"(可以译为"消息线程")。

    在JavaScript中,主线程在执行过程中遇到了异步任务,就发起函数或者称为注册函数,通过event loop线程通知相应的工作线程(如ajax,dom,setTimout等),同时主线程继续向后执行,不会等待。等到工作线程完成了任务,eventloop线程会将消息添加到消息队列中,如果此时主线程上调用栈为空就执行消息队列中排在最前面的消息,依次执行。 新的消息进入队列的时候,会自动排在队列的尾端。单线程意味着js任务需要排队,如果前一个任务出现大量的耗时操作,后面的任务得不到执行,任务的积累会导致页面的“假死”。这也是js编程一直在强调需要回避的“坑”。主线程会循环上述步骤,EventLoop就是主线程重复从消息队列中取消息、执行的过程。

    3.宏任务、微任务

    宏任务有:script、setTimeout、setInterval、setImmediate(浏览器暂时不支持,只有IE10支持)、I/O、UI Rendering。

    微任务有:Process.nextTick(node)、Promise、MutationObserver。

    代码题

    一、将下面异步代码使用Promise的方式改进

    setTimeout(function(){
        var a = 'hello'
        setTimeout(function(){
            var b = 'lagou'
            setTimeout(function(){
                var c = 'I ❤ U'
                console.log(a + b + c)
            },10)
        },10)
    },10)
    

    实现:

    function fn(value) {
        const promise = new Promise((resolved, rejected) => {
           setTimeout(()=>resolved(value), 10)
        });
        return promise;
     }
     
     fn()
        .then(() => fn("hello"))
        .then(value => fn(value + "lagou"))
        .then(value => fn(value + "I ❤ U"))
        .then(value => console.log(value))
    
    

    二、基于以下代码完成下面的四个练习

    const fp = require('lodash/fp')
    // 数据
    // horsepower 马力, dollar_value 价格, in_stock 库存
    const cars = [
        { name: 'Ferrari FF', horsepower: 660,
        dollar_value: 700000, in_stock: true },
        { name: 'Spyker C12 Zagato', horsepower: 650,
        dollar_value: 648000, in_stock: false },
        { name: 'Jaguar XKR-S', horsepower: 550,
        dollar_value: 132000, in_stock: false },
        { name: 'Audi R8', horsepower: 525,
        dollar_value: 114200, in_stock: false },
        { name: 'Aston Martin One-77', horsepower: 750,
        dollar_value: 1850000, in_stock: true },
        { name: 'Pagani Huayra', horsepower: 700,
        dollar_value: 1300000, in_stock: false },
    ]
    

    练习1:使用函数组合fp.flowRight()重新实现下面这个函数

    let isLastInStock = function (cars) {
        // 获取最后一条数据
        let last_car = fp.last(cars)
        // 获取最后一条数据的 in_stock 属性值
        return fp.prop('in_stock', last_car)
    }
    

    实现:

    let isLastInStock = fp.flowRight(fp.prop('in_stock'), fp.last)
    

    练习2:使用fp.flowRight()、fp.prop()和fp.first()获取第一个car的name

    实现:

    let isFirstCarName = fp.flowRight(fp.prop('name'), fp.first)
    

    练习3:使用帮助函数_average重构averageDollarValue,使用组合函数的方式实现

    let _average = function (xs) {
        return fp.reduce(fp.add, 0, xs)/xs.length
    } // <-无须改动
    let averageDollarValue = function (cars){
        let dollar_values = fp.map(function(car){
            return car.dollar_value
        }, cars)
        return _average(dollar_values)
    }
    

    实现:

    let averageDollorValue = fp.flowRight(_average, fp.map(fp.curry(fp.prop)('dollar_value')))
    

    练习4:使用flowRight写一个sanitizeNames()函数返回一个下划线连接的小写字符串,把数组中的name转换为这种形式:例如:sanitizeNames(["Hello World"])=>["hello_world"]

    let _underscore = fp.replace(/\W+/g, '_') // <-- 无须改动,并在 sanitizeNames中使用它
    

    实现:

    let sanitizeNames = fp.flowRight(fp.map(_underscore), fp.map(fp.toLower), fp.map(car => car.name))
    

    三、基于下面提供的代码,完成后续的四个练习

    // support.js
    class Container {
        static of(value) {
            return new Container(value)
        }
        constructor(value) {
            this.value = value
        }
        map(fn) {
            return Container.of(fn(this.value))
        }
    }
    class Maybe {
        static of(x) {
            return new Maybe(x)
        }
        isNothing() {
            return this._value === null || this._value === undefined
        }
        constructor(x) {
            this._value = x
        }
        map(fn) {
            return this.isNothing() ? this : Maybe.of(fn(this._value))
        }
    }
    module.exports = { Maybe, Container }
    

    练习1:使用fp.add(x,y)和fp.map(f,x)创建一个能让functor里的值增加的函数ex1

    // app.js
    const fp = require('lodash/fp')
    const { Maybe, Container } = require('./support')
    let maybe = Maybe.of([5, 6, 1])
    let ex1 = () => {
        // 你需要实现的函数。。。
    }
    

    实现:

    let ex1 = () => {
        return maybe.map(arr => fp.map(v => fp.add(v, 1), arr))
    }
    

    练习2:实现一个函数ex2,能够使用fp.first获取列表的第一个元素

    // app.js
    const fp = require('lodash/fp')
    const { Maybe, Container } = require('./support')
    let xs = Container.of(['do', 'ray',
    'me', 'fa', 'so', 'la', 'ti', 'do'])
    let ex2 = () => {
        // 你需要实现的函数。。。
    }
    

    实现:

    let ex2 = () => {
        return xs.map(fp.first).value
    }
    

    练习3:实现一个函数ex3,使用safeProp和fp.first找到user的名字的首字母

    // app.js
    const fp = require('lodash/fp')
    const { Maybe, Container } = require('./support')
    let safeProp = fp.curry(function (x, o){
        return Maybe.of(o[x])
    })
    let user = { id:2, name: 'Albert' }
    let ex3 = () => {
        // 你需要实现的函数。。。
    }
    

    实现:

    let ex3 = () => {
        return safeProp('name', user).map(fp.first)._value
    }
    

    练习4:使用Maybe重写ex4,不要有if语句

    // app.js
    const fp = require('lodash/fp')
    const { Maybe, Container } = require('./support')
    let ex4 = function (n) {
        if(n) {
            return parseInt(n)
        }
    }
    

    实现:

    let ex4 = n => Maybe.of(n).map(parseInt)._value;
    

    四、手写实现MyPromise源码

    要求:尽可能还原Promise中的每一个API,并通过注释的方式描述思路和原理

    const PENDING = 'pending';
    const FULFILLED = 'fulfilled';
    const REJECTED = 'rejected';
    
    class MyPromise {
        constructor (excutor) {
            try {
                excutor(this.resolve, this.reject);
            } catch (error) {
                this.reject(error)
            }
        }
        status = PENDING;
        value = undefined;
        reason = undefined;
        // 成功回调
        successCallback = [];
        // 失败回调
        failCallback = [];
        resolve = value => {
            if (this.status !== PENDING) return;
            this.status = FULFILLED;
            this.value = value;
            // this.successCallback && this.successCallback(value);
            while(this.successCallback.length) this.successCallback.shift()()
        }
        reject = reason => {
            if (this.status !== PENDING) return;
            this.status = REJECTED;
            this.reason = reason;
            // this.failCallback && this.failCallback(reason);
            while(this.failCallback.length) this.failCallback.shift()()
        }
        then (successCallback, failCallback) {
            successCallback = successCallback ? successCallback : value => value;
            failCallback = failCallback ? failCallback : reason => { throw reason };
            let promise2 = new MyPromise((resolve, reject) => {
                if (this.status === FULFILLED) {
                    setTimeout(() => {
                        try {
                            let x = successCallback(this.value);
                            // 判断 x 的值是普通值还是promise对象
                            // 如果是普通值,直接调用resolve
                            // 如果是promise对象 查看promise对象的返回结果
                            // 再根据promise对象的返回结果,决定调用resolve 还是reject
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                } else if (this.status === REJECTED) {
                    setTimeout(() => {
                        try {
                            let x = failCallback(this.reason);
                            // 判断 x 的值是普通值还是promise对象
                            // 如果是普通值,直接调用resolve
                            // 如果是promise对象 查看promise对象的返回结果
                            // 再根据promise对象的返回结果,决定调用resolve 还是reject
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                } else {
                    this.successCallback.push(() => {
                        setTimeout(() => {
                            try {
                                let x = successCallback(this.value);
                                // 判断 x 的值是普通值还是promise对象
                                // 如果是普通值,直接调用resolve
                                // 如果是promise对象 查看promise对象的返回结果
                                // 再根据promise对象的返回结果,决定调用resolve 还是reject
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    });
                    this.failCallback.push(() => {
                        setTimeout(() => {
                            try {
                                let x = failCallback(this.reason);
                                // 判断 x 的值是普通值还是promise对象
                                // 如果是普通值,直接调用resolve
                                // 如果是promise对象 查看promise对象的返回结果
                                // 再根据promise对象的返回结果,决定调用resolve 还是reject
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    });
                }
            });
            return promise2;
        }
    
        finally (callback) {
            return this.then(value => {
                return MyPromise.resolve(callback()).then(() => value);
            }, reason => {
                return MyPromise.resolve(callback()).then(() => { throw reason});
            })
        }
    
        catch(failCallback) {
            return this.then(undefined, failCallback);
        }
    
        static all (array) {
            let result = [];
            let index = 0;
            return new MyPromise((resolve, reject) => {
                function addData(key, value) {
                    result[key] = value;
                    index++;
                    if (index === array.length) {
                        resolve(result);
                    }
                 }
    
                 for (let i = 0; i < array.length; i++) {
                     let current = array[i];
                     if (current instanceof MyPromise) {
                        current.then(value => addData(i, value), reaseon => reject(reason));
                     } else {
                         addData(i, current)
                     }
                 }
            })
        }
    
        static resolve (value) {
            if (value instanceof MyPromise) return value;
            return new MyPromise(resolve => resolve(value));
        }
    }
    
    function resolvePromise(promise2, x, resolve, reject) {
        if (promise2 === x) {
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
        }
        if (x instanceof MyPromise) {
            // x.then(value => resolve(value), reason => reject(reason))
            x.then(resolve, reject);
        } else {
            resolve(x);
        }
    }
    
    module.exports = MyPromise
    

    起源地下载网 » 函数式编程与JS异步编程、手写Promise作业

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元