函数式编程与JS异步编程、手写Promise作业

293 阅读6分钟

简答题

一、谈谈你是如何理解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