# fed-e-task-01-01 **Repository Path**: rpyoyo/fed-e-task-01-01 ## Basic Information - **Project Name**: fed-e-task-01-01 - **Description**: No description available - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-08-20 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 简答题 ### 一、谈谈你是如何理解JS异步编程的,EventLoop、消息队列都是做什么的,什么是宏任务,什么是微任务? + **如何理解JS异步编程** + 由于JS是单线程的,同一时间只能执行一个任务,同步代码执行过程中,碰到耗时任务,会阻塞程序执行,异步编程模式使得执行耗时任务的场景得到优化,使JS执行更加高效 + 异步编程随着JS标准的发展,有很多种模式 callback-> Promise -> Generator -> async/await + 传统callback回调函数模式,简单、容易理解,但是容易产生嵌套问题(回调地狱),使代码可读性降低,变得难以维护 + Promise采用链式写法进行异步编程,避免里嵌套问题,同时提供了异常处理等更强大的功能 + Generator更像是一个状态机,可以将函数分步阻塞,需要主动调用next()才能执行下一步,实现了采用同步的方式来编写异步代码的方式,缺点是无法自动执行,需要一个执行器函数 + async/await是Generator的语法糖,不需要执行器可以自动执行,相较于Generator也更加语义化 + **EventLoop、消息队列都是做什么的** + 同步任务依次加入执行栈中执行,异步任务则交由另外的线程维护执行,当异步任务执行完成(或者event被触发),相应的回调函数会被加入到消息队列中等待执行,当执行栈中的同步任务都执行完成,则会查看消息队列中是否存在待执行的任务,加入执行栈中,这样的步骤依次循环,即为EventLoop事件循环 + **什么是宏任务、什么是微任务** + 执行栈中每次执行的任务即为宏任务(包括同步代码),setTimeout、setInterval等属于宏任务 + 微任务是宏任务执行过程中产生的异步任务,Promise.then、process.nextTick等属于微任务 + 调用栈中的任务(宏任务)执行完成后,会优先查看是否存在微任务,在将所有微任务依次执行完成后,查看消息队列中是否存在任务,取第一个加入调用栈,并开始下一轮事件循环,微任务会在上一轮事件循环的最后被执行 ## 代码题 ### 一、将下面异步代码使用Promise的方式改进 ```javascript setTimeout(function() { var a = 'hello' console.log(a) setTimeout(function() { var b = 'lagou' console.log(b) setTimeout(function() { var c = 'I ❤ U' console.log(a + b + c) }, 10) }, 10) }, 10) ``` 改写后如下,详见code\1.js ```javascript const p = value => new Promise(resolve => { setTimeout(()=>{ resolve(value) }, 10) }) p('hello') .then(value => p(value + 'lagou')) .then(value => p(value + 'I ❤ U')) .then(console.log) ``` ### 二、基于以下代码完成下面的四个练习 ```javascript 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: 'Jarguar 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: 185000, in_stock: true }, { name: 'Pagani Huayra', horsepower: 700, dollar_value: 130000, in_stock: false }, ] ``` #### 练习1:使用函数组合fp.flowRight()重新实现下面这个函数 ```javascript let isLastInStock = function (cars) { let last_car = fp.last(cars) return fp.prop('in_stock', last_car) } ``` 改写后,详见code\2.js ```javascript let isLastInStock_composed = fp.flowRight(fp.prop('in_stock'), fp.last) ``` #### 练习2:使用fp.flowRight()、fp.prop()、fp.first()获取第一个car的name 详见code\2.js ```javascript fp.flowRight(fp.prop('name'), fp.first)(cars) ``` #### 练习3:使用帮助函数_average重构averageDollarValue,使用函数组合的方式实现 ```javascript 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) } ``` 改写后如下,详见code\2.js ```javascript let averageDollarValue_composed = fp.flowRight(_average, fp.map(fp.prop('dollar_value'))) ``` #### 练习4:使用flowRight写一个sanitizeNames()函数,返回一个下划线连接的小写字符串,把数组中的name转换为这种形式:例如:sanitizeNames(['Hello World']) => ['hello_world'] 详见code\2.js ```javascript let _underscore = fp.replace(/\W+/g, '_') let sanitizeNames = fp.map(fp.flowRight(_underscore, fp.lowerCase)) ``` ### 三、基于下面提供的代码,完成后续的四个练习 ```javascript 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(value) { return new MayBe(value) } isNothing() { return this._value === null || this._value === undefined } constructor(value) { this._value = value } 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 详见code\3.js ```javascript let ex1 = () => { return maybe.map(fp.map(fp.add(1))) } ``` #### 练习2:实现一个函数ex2,能够使用fp.first()获取列表的第一个元素 详见code\3.js ```javascript let ex2 = () => { return xs.map(fp.first)._value } ``` #### 练习3:实现一个函数ex3,使用safeProp和fp.first找到user的名字的首字母 详见code\3.js ```javascript let ex3 = () => { return safeProp('name')(user).map(fp.first)._value } ``` #### 练习4:使用MayBe重写ex4,不要有if语句 详见code\3.js ```javascript let ex4_ = function (n) { // isNothing 与 if(n) 判断的差异,纯用MayBe达不到一致效果 // 用三元表达式似乎又没意义,和用if有什么差别呢? return !!n? parseInt(MayBe.of(n)._value) : undefined } ``` ### 四、手写实现MyPromise源码 #### 要求:尽可能还原Promise中的每一个API,并通过注释的方式描述思路和原理 详见code\promise.js