## 简答题
#### 谈谈你是如何理解 JS 异步编程的,EventLoop、消息队列都是做什么的,什么是宏任务,什么是微任务?
**答:**
js异步编程:提高效率,减少阻塞,不需要等待就可以执行其他任务
eventLoop:负责连接主线程和其他线程之间的通信
消息队列:异步任务处理完成会把任务放入到任务队列里面等待执行
微任务: 当前任务结束后立即执行其他任务不需要通过消息队列或者eventLoop promise 回调就是微任务 本轮调用末尾直接执行
宏任务: 消息队列里的异步任务都是宏任务 settimeout 回调就是一个宏任务 进入回调队列排队
/*
将下面异步代码使用 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) {
return new Promise((reslove,reject) =>{
setTimeout(()=>{
reslove(msg)
},10)
})
}
fn().then(()=>{
return fn('hello')
}).then(value => {
return fn(value+'lagou')
}).then(value => {
return 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: 625,
dollar_value: 114200,
in_stock: false
},
{
name: 'Aston Martin One-7',
horsepower: 750,
dollar_value: 1850000,
in_stock: true
},
{
name: 'Pagani Huayara',
horsepower: 700,
dollar_value: 1300000,
in_stock: true
}
]
/*
练习1:
let last_car = fp.last(cars) 获取最后一条数据
fp.prop('in_stock', last_car) 获取最后一条数据的 in_stock 属性值
实现 isLastInStock 函数, 要求使用 fp.flowRight()
把 fp.last(), fp.prop() 组合而成
*/
// 1.实现 isLastInStock 函数
let isLastInStock = fp.flowRight(fp.prop('in_stock'),fp.last)
// 2.打印测试
console.log(isLastInStock(cars)) // 最终返回 true
/*
练习2:
实现 firstName 函数, 要求使用 fp.flowRight()
把 fp.prop(), fp.first() 组合而成
*/
// 1.实现 firstName 函数
let firstName = fp.flowRight(fp.prop('name'), fp.first)
// 2.打印测试
console.log(firstName(cars)) // 最终返回 Ferrari FF (第一个 car 的 name)
/*
练习3:
实现函数 averageDollarValue, 要求使用 fp.flowRight()
把 fp.map(), _average() 组合而成
参考代码:
let averageDollarValue = function (cars) {
let dollar_values = fp.map(function (car) {
return car.dollar_value
}, cars)
return _average(dollar_values)
}
*/
let _average = function (xs) {
return fp.reduce(fp.add, 0, xs) / xs.length
} // <- 无须改动
// 1.实现 averageDollarValue 函数
let averageDollarValue = fp.flowRight(_average,fp.map(item => item.dollar_value))
// 2.打印测试
console.log(averageDollarValue(cars)) // 最终返回
/*
练习4:
实现 sanitizeNames() 函数,要求使用 fp.flowRight()
最终把 cars 当作参数, 应该得到下面结果
[
'ferrari_ff',
'spyker_c12_zagato',
'jaguar_xkr-s',
'audi_r8',
'aston_martin_one_7',
'pagani_huayara'
]
即:
1) 生成名字数组
2) 名字全部小写
3) 空格等字符替换成下划线 (直接使用 _underscore 即可)
*/
// 把非字母数字替换为下划线
let _underscore = fp.replace(/\W+/g, '_') // <--无须改动
// 1.实现 sanitizeNames 函数
let sanitizeNames = fp.map(fp.flowRight(fp.toLower,_underscore, item=>item.name))
// 2.打印测试
console.log(sanitizeNames(cars))
/*
[
'ferrari_ff',
'spyker_c12_zagato',
'jaguar_xkr-s',
'audi_r8',
'aston_martin_one_7',
'pagani_huayara'
]
*/
const fp = require('lodash/fp')
// const { Maybe, Container } = require('support')
/*
练习1:
实现函数 ex1
使用 fp.add(x, y) 和 fp.map(f, x)
让函子里的值 增加1
*/
class MayBe {
static of (value) {
return new MayBe(value)
}
constructor (value) {
this._value = value
}
map (fn) {
return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value))
}
isNothing () {
return this._value === null || this._value === undefined
}
}
// 1.创建一个函子
let maybe = MayBe.of([5, 6, 1])
// 2.实现 ex1 函数
let ex1 = fp.map(fp.add(1))
// 3.调用测试
console.log( maybe.map(ex1) ) // Maybe { _value: [ 6, 7, 2 ] }
/*
练习2:
实现 ex2 函数
函数中使用 fp.first 获取列表的第一个元素
*/
class Container {
static of (value) {
return new Container(value)
}
constructor (value) {
this._value = value
}
map (fn) {
return Container.of(fn(this._value))
}
}
// 1.生成一个函子
let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do'])
// 2.实现 ex2
let ex2 = fp.first
// 3.测试打印
console.log( xs.map(ex2) ) // Container { _value: 'do' }
/*
练习3:
实现 ex3 函数
使用 safeProp 和 fp.first 找到 user 的名字的首字母
*/
let safeProp = fp.curry(function (x, o) {
return MayBe.of(o[x])
})
let user = { id: 2, name: 'Albert'}
// 1.实现 ex3
let ex3 = function(){
return safeProp('name',user).map(fp.first)
}
// 2.测试打印
console.log( ex3() ) // Maybe { _value: 'A' }
/*
练习4:
使用 Maybe 重写 ex4, 不要有 if 语句
let ex4 = function (n) {
if (n) {
return parseInt(n)
}
}
功能描述:
把参数中数值部分转为整数,并且以函子形式返回
换言之, 提取字符串里的数字作为返回结果函子的 _value 值
*/
// 1.实现 ex4 函数
let ex4 = (n) =>{
let a = new MayBe(n);
let b = a.map(parseInt)
return b
}
// 2.测试打印
console.log( ex4('7R') ) // Maybe { _value: 7 }
console.log( ex4('7.6B')) // Maybe { _value: 7 }
console.log( ex4('8.2G') ) // Maybe { _value: 8 }
console.log( ex4(null) ) // Maybe { _value: null }
console.log( ex4(undefined) ) // Maybe { _value: undefined }
console.log( ex4('i7.5') ) // Maybe { _value: NaN }
console.log( ex4('abc') ) // Maybe { _value: NaN }
手写Promise
/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/
//声明构造函数
function Promise(executor){
//添加属性
this.PromiseState = 'pending';
this.PromiseResult = null;
//声明属性
this.callbacks = [];
//保存实例对象的 this 的值
const _this = this;
//resolve 函数
function resolve(data){
//判断状态
if(_this.PromiseState !== 'pending') return;
//1. 修改对象的状态 (promiseState)
_this.PromiseState = 'fulfilled';// resolved
//2. 设置对象结果值 (promiseResult)
_this.PromiseResult = data;
//调用成功的回调函数
setTimeout(() => {
_this.callbacks.forEach(item => {
item.onResolved(data);
});
});
}
//reject 函数
function reject(data){
//判断状态
if(_this.PromiseState !== 'pending') return;
//1. 修改对象的状态 (promiseState)
_this.PromiseState = 'rejected';//
//2. 设置对象结果值 (promiseResult)
_this.PromiseResult = data;
//执行失败的回调
setTimeout(() => {
_this.callbacks.forEach(item => {
item.onRejected(data);
});
});
}
try{
//同步调用『执行器函数』
executor(resolve, reject);
}catch(e){
//修改 promise 对象状态为『失败』
reject(e);
}
}
//添加 then 方法
Promise.prototype.then = function(onResolved, onRejected){
const _this = this;
//判断回调函数参数
if(typeof onRejected !== 'function'){
onRejected = reason => {
throw reason;
}
}
if(typeof onResolved !== 'function'){
onResolved = value => value;
//value => { return value};
}
return new Promise((resolve, reject) => {
//封装函数
function callback(type){
try{
//获取回调函数的执行结果
let result = type(_this.PromiseResult);
//判断
if(result instanceof Promise){
//如果是 Promise 类型的对象
result.then(v => {
resolve(v);
}, r=>{
reject(r);
})
}else{
//结果的对象状态为『成功』
resolve(result);
}
}catch(e){
reject(e);
}
}
//调用回调函数 PromiseState
if(this.PromiseState === 'fulfilled'){
setTimeout(() => {
callback(onResolved);
});
}
if(this.PromiseState === 'rejected'){
setTimeout(() => {
callback(onRejected);
});
}
//判断 pending 状态
if(this.PromiseState === 'pending'){
//保存回调函数
this.callbacks.push({
onResolved: function(){
callback(onResolved);
},
onRejected: function(){
callback(onRejected);
}
});
}
})
}
//添加 catch 方法
Promise.prototype.catch = function(onRejected){
return this.then(undefined, onRejected);
}
//添加 resolve 方法
Promise.resolve = function(value){
//返回promise对象
return new Promise((resolve, reject) => {
if(value instanceof Promise){
value.then(v=>{
resolve(v);
}, r=>{
reject(r);
})
}else{
//状态设置为成功
resolve(value);
}
});
}
//添加 reject 方法
Promise.reject = function(reason){
return new Promise((resolve, reject)=>{
reject(reason);
});
}
//添加 all 方法
Promise.all = function(promises){
//返回结果为promise对象
return new Promise((resolve, reject) => {
//声明变量
let count = 0;
let arr = [];
//遍历
for(let i=0;i<promises.length;i++){
//
promises[i].then(v => {
//得知对象的状态是成功
//每个promise对象 都成功
count++;
//将当前promise对象成功的结果 存入到数组中
arr[i] = v;
//判断
if(count === promises.length){
//修改状态
resolve(arr);
}
}, r => {
reject(r);
});
}
});
}
//添加 race 方法
Promise.race = function(promises){
return new Promise((resolve, reject) => {
for(let i=0;i<promises.length;i++){
promises[i].then(v => {
//修改返回对象的状态为 『成功』
resolve(v);
},r=>{
//修改返回对象的状态为 『失败』
reject(r);
})
}
});
}