简答题
一、谈谈你是如何理解JS异步编程的,EventLoop、消息队列都是做什么的,什么是宏任务,什么是微任务?
js异步编程
因为早起的js主要是处理页面的交互,其核心就是DOM操作,这决定了js的引擎是以单线程模式执行js代码。
这就会出现一个问题,假如在执行的过程中,需要了一个耗时特别就的任务,就会阻塞后面的代码执行。
为了解决这耗时任务阻塞执行的问题,js将任务的执行模式分成了两种:
同步模式,异步模式。
同步模式,就是任务排队依次执行。
异步模式,就是通过回调函数的形式将所要执行的函数,放到任务队列中,不占用主线程,只有等主线程执行完毕后,才通知请求执行任务,然后才将任务从任务队列中加入到主线程中执行。
EventLoop
事件循环,是我们使用异步的原理,是指浏览器一种解决JS单线程运行时的一种机制。
它主要是负责监听调用栈Call stack和消息队列
一旦Call stack调用栈里面的任务结束了,它就开始工作了。
消息队列
消息队列是一种数据结构,主要是存放那些异步的回调任务的。
宏任务&微任务
ES6 规范中,microtask 称为 jobs,macrotask 称为 task
宏任务是由宿主发起的,消息队列中的每个任务都是宏任务,而微任务由JavaScript自身发起。
执行栈在完成同步任务之后,会去执行任务队列中的宏任务,每次宏任务执行完毕之后,查看相应的微任务队列是否为空,如果不为空,则会按照先进先出的规则全部执行完对应的微任务,如此循环,直至任务结束。
代码题
一、将下面异步代码使用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((resolve, reject) => {
setTimeout(() => {
resolve(value)
}, 10)
})
return promise
}
fn().then(() => {
return fn('hello')
}).then((value) => {
return fn(value + 'lagou')
}).then((value) => {
return fn(value + 'I ❤️ U')
}).then((value) => {
console.log(value)
})
// 另一种方式
Promise.resolve()
.then(()=>{
return 'hello'
})
.then(value=>{
return value + 'lagou'
})
.then(value=>{
console.log(value + 'I ❤️ U')
})
二、基于以下代码完成下面的四个练习
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)
console.log(isLastInStock(cars))
// 练习2:使用fp.flowRight()、fp.prop()和fp.first()获取第一个car的name
// => 解答
let firstCarName = fp.flowRight(fp.prop('name'), fp.first)
console.log(firstCarName(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)
}
// => 解答
let averageDollarValue = fp.flowRight(_average,fp.map(car => car.dollar_value))
console.log(averageDollarValue(cars))
// 练习4:使用flowRight写一个sanitizeNames()函数,返回一个下划线连接的小写字符串,把数组中的name转换为这种形式:
// 例如:sanitizeNames(["Hello World"]) => ["Hello_world"]
let _underscore = fp.replace(/\W+/g, '_') // => 无须改动
// => 解答
let sanitizeNames = fp.map(fp.flowRight(_underscore, fp.toLower));
console.log(sanitizeNames(["Hello World","Javascript Code"]) )
三、基于下面提供的代码,完成后续的四个练习
// 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 = (num) => {
let fn = fp.flowRight(fp.map(fp.add(num)))
return maybe.map(fn)
}
console.log(ex1(4))
// 练习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
}
console.log(ex2())
// 练习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
}
console.log(ex3())
// 练习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 = function (n) {
let m1 = new Maybe(n)
let m2 = m1.map(parseInt)
return m2._value
}
console.log(ex4())
console.log(ex4(34))
四、手写实现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 #<Promise> "))
}
if (x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
}
module.exports = MyPromise