大前端【1-1习题】函数式编程与JS异步编程、手写Promise

模块一:函数式编程与JS异步编程、手写Promise


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

答:

(1)JS异步编程

​ JavaScript将任务的执行模式分为两种,一种是同步执行模式,另一种就是JS异步编程执行模式,我们都知道,JavaScript执行的环境是单线程,即在同一时间只能做一件事情,代码依次执行,如下:

console.log("task1")
console.log("task2")
console.log("task3")
console.log("task4")

代码会依次输出,task1、task2、task3、task4,安全、直观简洁,但如果接口响应时间比较长,则会出现程序“假死”的情况,如下,很大程度上降低了执行效率速度,效率低,响应时间长

console.log("task start")
while(i<1000){ //夸张演示
   ajax("http://www.xxx.com/getData/i") //响应时间长
}
console.log("task end")

怎样做到异步编程?

回调函数。通过回调函数的形式将所要执行的函数放到任务队列中,不占用主线程,只有等主线程执行完毕之后,才通知请求执行任务,从而将任务从任务队列进入到主线程中执行。异步方案也在不断的发展,callback => promise => gennerator => async/await…

(2)Event Loop

Event Loop,顾名思义为:事件循环,是我们使用异步的原理,是指浏览器一种解决JS单线程运行时的一种机制。

console.log(0)
setTimeout(()=>{
  console.log(1)
},10)
console.log(2)
//结果:0、2、1

上图

大前端【1-1习题】函数式编程与JS异步编程、手写Promise_第1张图片

步骤分为:

  1. 同步任务在主线程执行,形成一个执行栈

  2. 在主线程之外,还存在一个任务队列,通过回调函数的形式将所要执行的函数放到任务队列中,不占用主线程

  3. 等主线程执行完毕后,系统会读取"任务队列",从而将任务从任务队列进入到主线程中执行。

  4. 主线程循环执行第三步

    主线程从任务队列中读取事件,这个过程是不断循环的,所以整个运行机制被称为event loop

(3)消息队列

栈、堆、消息队列是一种数据结构,队列,特点为先进先出,存放执行的任务

(4)宏任务和微任务

任务分为:宏任务和微任务

宏任务:消息队列中的每个任务都是宏任务,

微任务:每个宏任务对应都有一个微任务队列

大前端【1-1习题】函数式编程与JS异步编程、手写Promise_第2张图片

执行栈在完成同步任务之后,会去执行任务队列中的宏任务,每次宏任务执行完毕之后,查看相应的微任务队列是否为空,如果不为空,则会按照先进先出的规则全部执行完对应的微任务,如此循环,直至任务结束。

微任务主要解决任务优先级的问题和单个任务执行过长的问题

举个栗子:

setTimeout(function () {
   console.log('1')
}); //宏任务

new Promise(function (resolve) {
   console.log('2');
   resolve();
}).then(function () { //then、微任务
   console.log('3')
});

console.log('4');
//2、4、3、1

因为settimeout是宏任务,虽然先执行的他,但是他被放到了宏任务的消息队列里,然后代码继续往下检查看有没有微任务,检测到Promise的then函数被放入了微任务序列。等到主线进程的所有代码执行结束后。先从微任务queue里拿回掉函数,然后微任务queue空了后再从宏任务的queue拿函数。所以正确的执行结果是:2,4,3,1。

【代码题】一、将下边异步代码使用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(msg) {
   var promise = new Promise((resolved, rejected) => {
      setTimeout(()=>{
         resolved(msg)
      }, 10)
   });
   return promise;
}

fn().then(function () {
   return fn("hello")
}).then(value=> {
   return fn(value+"lagou")
}).then(value=>{
   return fn(value+"I ♥ U")
}).then(value=>{
   console.log(value)
})
【代码题】二、基于以下代码完成下面四个练习
const cars = [
    {name:'Ferrair FF',horsepower:660,dollar_value:700000,in_stock:true},
    {name:'Spyker C12 Zagato',horsepower:650,dollar_value:648000,in_stock:false},
    {name:'Jafuar 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 isLaskInStock = function (cars){
    let last_car = fp.last(cars)
    return fp.prop('in_stock',last_car)
}

答:

let fr = fp.flowRight(fp.prop('in_stock'),fp.last)
console.log(fr(cars)) // false

2、使用函数组合fp.flowRight()、fp.prop()和fp.first()获取第一个car的name

答:

let fr = fp.flowRight(fp.prop('name'),fp.first)
console.log(fr(cars)) // Ferrair FF

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 a = averageDollarValue(cars)
console.log(a)

答:

let  _average = function (xs){
    return fp.reduce(fp.add,0,xs) / xs.length
}

let averageDollarValue = function(cars){
   let arr = fp.flowRight(_average,fp.map(car=>car.dollar_value))
   return arr(cars)
}

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

let _underscore = fp.replace(/\W+/g,"_") // 无需改动

答:

function sanitizeNames(){
   return fp.flowRight(fp.map(name=>_underscore(name)))
}
// var name = ["hello world","bei jing"]
// console.log(sanitizeNames()(name)) // 結果[ 'hello_world', 'bei_jing' ]

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

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

const fp = require('lodash/fp')
const { parseInt } = require("lodash")

let maybe = MayBe.of([5,6,1])
let ex1 = () =>{
      //补充
}
console.log(ex1(3))

答:

let ex1 = (num) =>{
    let fn = fp.flowRight(fp.map(fp.add(num)))
    return maybe.map(fn)  
}
console.log(ex1(3)) // [8,9,4]

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

let xs = Container.of(['do','ray','me','fa','so','la','ti','do'])
let ex2 = () => {
    //你要实现的函数
}

答:

let ex2 = () => {
    return xs.map(fp.first)._value
}
console.log(ex2()) // do

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

答:

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

4、使用MayBe重写ex4,不要有if语句

let  ex4 = function(n){
    if(n){
        return parseInt (n)
    }
}

答:

let  ex4 = function(n){
    let m1 = new MayBe(n)
    let m2 = m1.map(parseInt)
    return m2._value
}
ex4()  //undefined
ex4(1) // 1

四、手写实现MyPromise源码

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

/**
 * 1、promise是一个类,在执行这个类的时候,需要传递一个执行器进去,执行器会立即执行
 * 2、Promise中有三个状态,分别为 成功 resolve、失败 reject、等待 pedding
 *    状态一旦确定就不能被改变
 *    pedding-resolve
 *    pedding-reject
 * 3、resolve和reject函数是用来更改状态的
 *    resolve:fufilled
 *    reject:rejected
 * 4、then方法做的事情就是判断状态,如果状态是成功,调用成功回调函数,如果是失败,调用失败函数,then方法是被定义在原型对象中
 * 5、then成功回调有一个参数,表示成功之后的值,失败回调有一个参数,表示失败的原因
 */
const PEDDING = 'pedding' //等待
const FUFILLED = 'fufilled' //成功
const REJECT = 'reject' //失败

class MyPromise {
    constructor(exeuctor) {
        try {
            exeuctor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }

    status = PEDDING
    //成功之后的值
    value = undefined
    //失败之后的原因
    reason = undefined
    //成功回调
    // successCallback = undefined  只能处理一个回调函数
    successCallback = []
    //失败回调
    // failCallback = undefined
    failCallback = []


    //使用箭头函数定义是为了执行方法的时候让this指向MyPromise的实例对象
    resolve = value => {
        //如果状态不是等待,向下执行
        if (this.status !== PEDDING) return
        this.status = FUFILLED
        //保存成功之后的值
        this.value = value
        //判断成功回调是否存在,如果存在则调用
        // this.successCallback && this.successCallback(this.value)
        while (this.successCallback.length) {
            // this.successCallback.shift()(this.value)
            this.successCallback.shift()()
        }
    }

    reject = reason => {
        if (this.status !== PEDDING) return
        this.status = REJECT
        //保存失败后的原因
        this.reason = reason
        // this.failCallback && this.failCallback(this.reason)
        while (this.failCallback.length) {
            // this.failCallback.shift()(this.reason)
            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 === FUFILLED) {
                // let x = successCallback(this.value)
                /**
                 * 需要判断x的值是普通值还是promise对象,如果是普通值,直接调用resolve,
                 * 如果是promise对象,查看promise的结果,根据promise对象返回的结果决定调用resolve,reject
                 */
                // resolvePromise(x,resolve,reject)
                //防止循环调用,但是此时promise2并不能获取到,所以现在需要使其变成异步执行代码
                // resolvePromise(promise2,x,resolve,reject)
                //使用try-catch捕获异常
                try {
                    setTimeout(() => {
                        let x = successCallback(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    }, 0)
                } catch (error) {
                    reject(error)
                }
            } else if (this.status === REJECT) {
                setTimeout(() => {
                    let x = failCallback(this.reason)
                    resolvePromise(promise2, x, resolve, reject)
                }, 0)
            } else {
                //状态为pedding,等待
                // 将成功回调和失败回调存储起来
                // this.successCallback.push(successCallback)
                this.successCallback.push(() => {
                    setTimeout(() => {
                        let x = successCallback(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    }, 0)
                })
                // this.failCallback.push(failCallback)
                this.failCallback.push(() => {
                    setTimeout(() => {
                        let x = failCallback(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    }, 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 = []
        
        return new MyPromise((resolve, reject) => {
            let count = 0
            function addData(index,value){
                result[index] = value
                count++
                console.log(count,array.length)
                if(count === array.length){
                    resolve(result) 
                }   
            }
            for(let i= 0;i<array.length;i++){
                let current = array[i]
                if(current instanceof MyPromise){
                    //Promise对象
                    current.then((value)=>{
                        addData(i,value)
                    },(reason)=>{
                        reject(reason)
                    })
                }else{//普通值
                    addData(i,current)
                }
            }
        })
    }


    static resolve(value){
        if(value instanceof MyPromise){
            return value
        }
        return new MyPromise(resolve=>resolve(value))
    }

}

function resolvePromise(promise, x, resolve, reject) {
    if (promise === x) {
        return reject(new TypeError("Chaining cycle detected for promise #    "))
    }
    if (x instanceof MyPromise) {
        x.then(resolve, reject)
    } else {
        resolve(x)
    }
}

module.exports = MyPromise

你可能感兴趣的:(拉钩教育,js,html5,es6,javascript,node.js)