1. 我是皮皮虾首页
  2. 编程开发
  3. 前端

面试记录之一二三的练习题

一、简答题

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

自己的不太全面的回答:

js的执行环境是单线程的,大部分都是同步执行的。

如果全部都是同步执行的话整个页面会比较慢,所以可以把一些不影响页面显示的程序去异步执行,所以这就用到了异步编程。

EventLoop:js代码都是自上而下依次执行,有一个事件执行栈,依次把事件推入执行栈

消息队列:遇到异步函数类似于settimeout这种,就暂时挂起,放在消息队列中,等到上一轮的宏任务和微任务执行完成之后再执行这一轮

宏任务可以理解为服务器的主线任务,微任务是宏任务执行之余附带的任务。常见的微任务有promise.then,node中的nexttick等等。
人家给出的答案:

JS异步编程:Javascript的执行环境是单线程的,一次只能执行一个任务,多任务需要排队等候,这种模式可能阻塞代码,导致执行效率低下。为了避免这个问题,出现异步编程。
一般是通过callback回调函数,promise,事件的发布/订阅等来组织代码,本质都是通过回调函数来实现异步代码的存放和执行


EventLoop、消息队列
EventLoop是一种循环机制,不断去轮询一些队列,从中找到需要执行的任务并按顺序执行的一个执行模型。
消息队列是用来存放宏任务的队列,比如定时器的时间到了,当前定时时间内传入的方法引用会存到该队列

一开始整个脚本作为一个宏任务执行。执行过程中同步代码直接执行,宏任务等待时间到达或者成功之后,将方法的回调放入宏任务队列中,微任务进入微任务队列
当前主线程的宏任务执行完出队,然后再check是否有未执行的微任务并执行完成。接着执行浏览器UI线程的渲染工作,检查web worker任务,有则执行


宏任务和微任务
宏任务可以理解为每次执行栈执行的,即每次从事件队列中获取一个时间回调并放到执行栈中执行
一个宏任务执行结束之后,页面会先渲染接着在执行下一个宏任务
宏任务:script整体代码,setTimeout,setInterval,I/O,UI交互事件,MessageChannel等

微任务可以理解是在当前任务执行结束后需要立即执行的任务。也就是说,在当前任务后,在渲染之前,执行清空微任务。所以它的响应速度相比宏任务会更快,因为无需等待 UI 渲染。

微任务包含:Promise.then、MutaionObserver、process.nextTick(Node.js 环境)等

二、代码题

// 将下面的异步代码使用Promise的方式改进-
setTimeout(function() {
    var a = 'hello'
    setTimeout(function() {
        var b = 'lagou'
        setTimeout(function() {
            var c = 'I ❤️ U'
            console.log(a + b + c)
        }, 5000);
    }, 10);
}, 10);

——————————解答分界线—————————–

// 自我思考版,感觉绕来绕去的  不知道是不是自己想复杂了,因为setTimeout里面的异步在then方法里面是获取不到值的,所以用all方法可以依次执行
let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        var a = 'hello'
        resolve(a)
    }, 10);
})
let p2 = p1.then((val)=>{
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            var b = 'lagou'
            resolve(val + b)
        }, 10);
    })  
})

let p3 = p2.then(val=>{
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            var c = 'I ❤️ U'
            resolve(val + c)
        }, 5000);
    })
    
})
Promise.all([p1, p2, p3]).then((val)=>{
    console.log('res', val[2])
})

// 我靠,果然是我想的太复杂,题目及其简单其实就是用promise代替settimeout而已,(我好菜啊)不知道我脑子里绕这么多弯做什么,重写
// ------------
let p = new Promise((resolve, reject)=> {
    var a = 'hello'
    resolve(a)
})
p.then(val=>{
    var b = 'lagou'
    return val+b
}).then(val=>{
    var c = 'I ❤️ U'
    console.log(val + c)
})
// ------------
async function getData(){
    let p1 = await Promise.resolve('hello')
    let p2 = await Promise.resolve('lagou')
    let p3 = await Promise.resolve('I ❤️ U')
    console.log(p1+p2+p3+'hahahah')
}
getData()
// ------------
// 基于以下代码完后才能下面的四个练习
const { first, last, prop } = require('lodash/fp')
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)
}
// -----answer-----
let isLastInStock = fp.flowRight(prop('in_stock'), last)
console.log(isLastInStock(cars))
// ----answer------



// 练习2:使用 fp.flowRight()、fp.prop() 和 fp.first() 获取第一个 car 的 name
let isLastInStock = fp.flowRight(prop('name'), first)
console.log(isLastInStock(cars))



/ 练习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)
}
console.log(averageDollarValue(cars))
// ----------没懂,但应该是考函数组合的知识点,因为对fp函数的一些语法不太熟悉,就不纠结了
const total = cars=>fp.map(cars=>cars.dollar_value,cars)
const fn = fp.flowRight(_average,total);
console.log(fn(cars))



// 练习4:使用 flowRight 写一个 sanitizeNames() 函数,返回一个下划线连续的小写字符串,把数组中的 name 转换为这种形式,例如:sanitizeNames(["Hello World"]) => ["hello_world"]
let _underscore = fp.replace(/\W+/g, '_') // 无须改动,并在 sanitizeNames 中使用它
// function split1(val){
//     return val.split(' ')
// }
let sanitizeNames = fp.flowRight(fp.map(_underscore),fp.map(fp.toLower),fp.map((car) => car.name))
console.log(sanitizeNames(cars))
基于下面提供的代码,完成后续的四个练习
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))
    }
}
const fp = require('lodash/fp')
// 练习1:使用 fp.add(x, y) 和 fp.map(f,x) 创建一个能让 functor 里的值增加的函数 ex1
let maybe = Maybe.of([5,6,11])
// 思路: 函子对象的 map 方法可以运行一个函数对值进行处理,函数的参数为传入 of 方法的参数;接着对传入的整个数组进行遍历,并对每一项执行 fp.add 方法
let ex1 = maybe.map(e => fp.map(fp.add(2), e) )
console.log(ex1)


// 练习2:实现一个函数 ex2,能够使用 fp.first 获取列表的第一个元素
let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do'])
let ex2 = xs.map(e=>{ fp.first(e)} )
console.log(ex2._value)


// 练习3:实现一个函数 ex3,使用 safeProp 和 fp.first 找到 user 的名字的首字母
let safeProp = fp.curry(function(x, o){
    return Maybe.of(o[x])
})
let user = { id: 2, name: 'Albert' }
// let ex3 = fp.flowRight(fp.map(i => fp.first(i)), safeProp('name'))
// let ex3 = fp.first(safeProp('name', user)._value)
let ex3 = safeProp('name', user).map(x => fp.first(x))
console.log(ex3)


// 练习4:使用 Maybe 重写 ex4,不要有 if 语句
let ex4 = function(n){
    if(n){
        return parseInt(n)
    }
}
// let ex4 = Maybe.of(34.24).map(e=>parseInt(e))
let ex4 = (value)=>{ return Maybe.of(value).map(e=>parseInt(e)) }
console.log(ex4(78.87))

最后一题:手写实现Promise源码

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

写好多次了,就不再贴了,学习记录之三刚好是,没有实现race方法,但是race方法也蛮简单。主要需要考虑一些promise的特质。

原创文章,作者:一苇,如若转载,请注明出处:https://wsppx.cn/901/%e7%bd%91%e7%bb%9c%e5%bc%80%e5%8f%91/%e5%89%8d%e7%ab%af/

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注