简答题
一、谈谈你是如何理解JS异步编程的,EventLoop、消息队列都是做什么的,什么是宏任务,什么是微任务?
-
JS是异步编程
JS是异步的原因是因为JS是单线程的,单线程就决定了JS一次只能够执行一个任务。如果遇见耗时任务的时候会导致耗时任务后面的代码进行堵塞,导致后续代码一直挂起,从而会给用户带来不好的交互体验。因次,JS也必须设计一种任务处理模式来解决这种耗时特性的任务,也就是异步任务。
那我们可能会想,为什么不能够设计为多线程模式拿?这是因为多线程会导致复杂的线程同步问题,比如说js一个线程对dom进行增加操作,另一个线程对dom进行删除操作,此时浏览器应该进行那个线程的操作? -
EventLoop
事件循环负责监听调用栈与消息队列的任务。如果当监听到调用栈中的任务空的时候,事件循环会去看看消息队列中是否有排队任务,如果有任务会将队列中的第一个任务压入调用栈执行从而开启新的一轮循环。每轮循环结束之后会执行一些必要的渲染和绘制操作。 -
消息队列
当调用栈有异步任务(异步API)的时候,异步任务会交给浏览器另一个线程(异步线程)来处理异步任务,当异步任务完成时,如定时器倒计时结束之后,会将该异步任务的回调添加到消息队列中(事件队列),等待事件循环将其压入调用栈。
如果按照宏任务与微任务来解释JS执行机制,消息队列又会分为宏任务队列与微任务队列。将整段sciprt标签中的代码当作宏任务压入callStack中执行,执行过程中碰见宏任务会将宏任务添加到消息队列中,碰见微任务会将微任务添加消息队列中,当当前宏任务执行完之后,事件循环会区看看微任务队列中是否有微任务没有执行,如果有的话,会将所有的微任务压入调用栈执行。如果遇到微任务中有宏任务的情况,会将宏任务添加到宏任务消息队列等待执行,如果微任务中还有微任务会添加到微任务队列并再本轮循环执行。 -
宏任务、微任务
我也不知道该怎么解释究竟什么是宏任务微任务的概念,老师帮忙解释下。
我就讲讲我所知道的宏任务与微任务。
宏任务:
- setTimeout,setInterval,setImmediate(非标,仅在node中与ie支持)
- sciprt标签
- 注册的事件
微任务: - Promise
- MutationObserver
- queueMicrotask(() => {
console.log(‘微任务’)
})
宏任务微任务执行机制已经再 第三题已经解释过了
老师帮忙解答一个疑问:
我们再尝试解释JS执行机制是不是可以通过两个维度来解释:
- 一个从宏任务与微任务来解释
- 从主线程和异步线程来解释
他们所指的都是同一件事情?我们面试的时候应该怎么解释拿?
对应下面两张图
代码题
二、将下面异步代码使用Promise的方式改进
// 分析: 这行代码的目的是输入 a + b + c && 解决回调地狱
// 解:
const promise = new Promise((resolve, reject) => {
setTimeout(function () {
resolve('hello')
}, 10)
})
promise.then(res => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(res + 'lagou')
}, 10)
})
}).then(res => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(res + 'I ♥ U')
}, 10)
})
}).then(res => {
console.log(res, 'promise')
})
三、基于以下代码完成下面的四个联系
// 练习1: 使用函数组合fp.flowRight() 重新实现下面这个函数
let isLastInStock = function (cars) {
let last_car = fp.last(cars)
console.log(last_car)
return fp.prop('in_stock', last_car)
}
解:
const isLastInStock = fp.flowRight(fp.prop('in_stock'),fp.last)
console.log(isLastInStock(cars))
// 练习2: 使用 fp.flowRight()、fp.prop() 和 fp.first() 获取第一个 car 的 name
// 解:
const getFirstCarName = fp.flowRight(fp.prop('name'),fp.first)
console.log(getFirstCarName(cars))
// 练习3: 使用帮助函数_average 重构 averageDollarValue, 使用函数组合方式实现
// 求每辆车的平均价钱
let _average = function (xs) {
// fp.add 返回两个数的总和
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)
}
// 第一种
const getAverageDollarValue = fp.flowRight(_average, fp.map(car => car.dollar_value))
// 第二种
const getAverageDollarValue = fp.flowRight(_average, fp.map(fp.curry(fp.prop)('dollar_value')))
console.log(getAverageDollarValue(cars), 'll')
// 练习4: 使用 flowRight 写一个 sanitizeNames() 函数,返回一个下划线连接的小写字符串,把数组中的 name 转换为这种形式:例如:sanitizeNames(["Hello World"]) => ["hello_world"]
let _underscore = fp.replace(/\W+/g, '_') // 无需改动,并在sanitizeNames中使用他
const sanitizeNames = fp.flowRight(fp.map(_underscore),fp.map(fp.prop('name')))
console.log(sanitizeNames(cars))
函子
四、基于下面提供的代码完成后续练习
题目
// 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)
}
constructor(x) {
this._value = x
}
isNothing() {
return this._value === null || this._value === undefined
}
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('loadsh/fp')
const { Maybe, Container } = require('./support.js')
let maybe = Maybe.of([5, 6, 1])
console.log(maybe.map(ex1))
function ex1(value) {
console.log(value)
return fp.map((x) => x + 1, value) // Maybe { _value: [ 6, 7, 2 ] }
}
// 练习2:实现一个函数ex2, 能够使用fp.first获取列表的第一个元素
const fp = require('loadsh/fp')
const { Maybe, Container } = require('./support.js')
let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do'])
let ex2 = (value) => {
return fp.first(value)
}
console.log(xs.map(ex2)) // Container { _value: 'do' }
// 练习3:实现一个函数ex3, 使用safeProp 和fp.first找到user的名字的首字母
const fp = require('loadsh/fp')
const { Maybe, Container } = require('./support.js')
let safeProp = fp.curry(function (x, o) {
return Maybe.of(o[x])
})
let user = {id: 2, name: 'Albert'}
// 取出函子的值
let ex3 = (value) => {
return value._value
}
const compose = fp.flowRight(fp.first,ex3,safeProp('name'))
console.log(compose(user)) // A
// 练习3:实现一个函数ex3, 使用safeProp 和fp.first找到user的名字的首字母
const fp = require('loadsh/fp')
const { Maybe, Container } = require('./support.js')
// let ex4 = function (n) {
// // 这里的if 因为是避免因为n导致整个函数不纯。
// if (n) {
// return parseInt(n)
// }
// }
// 使用maybe函子处理
let ex4 = function (n) {
// maybe函子对外部传递的空值进行处理
return Maybe.of(n).map(n => parseInt(n))
}
console.log(ex4(null))
// 也可以利用either函子来解决此问题
// 处理异常
// class Left {
// static of(val) {
// return new Left(val)
// }
// constructor(val) {
// this._value = val
// }
// map(fn) {
// return this
// }
// }
// // 整场执行
// class Right {
// static of(val) {
// return new Right(val)
// }
// constructor(val) {
// this._value = val
// }
// map(fn) {
// return Right.of(fn(this._value))
// }
// }
// let ex4 = function (n) {
// try {
// return Right.of(parseInt(n))
// } catch (err) {
// return Left.of({ err })
// }
// }
五、手写promise
https://blog.csdn.net/weixin_51457459/article/details/115023523