# fed-e-task-01-01 **Repository Path**: gdfanxing/fed-e-task-01-01 ## Basic Information - **Project Name**: fed-e-task-01-01 - **Description**: part1模块一作业 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-08-30 - **Last Updated**: 2021-11-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # fed-e-task-01-01 # part1模块一作业 # 模块一:函数式编程与JS异步编程、手写Promise ### 简答题 #### 一、谈谈你是如何理解JS异步编程的,EvenLoop、消息队列都是做什么的,什么是宏任务,什么是微任务? - 异步编程 javascript语言的一大特点就是单线程,在某个特定的时刻只有特定的代码能够被执行,并阻塞其它的代码,也就是说,同一个时间只能做一件事。为了避免这个问题,出现了异步编程。一般是通过 callback 回调函数、事件发布/订阅、Promise 等来组织代码,本质都是通过回调函数来实现异步代码的存放与执行。 - EvenLoop EvenLoop(事件循环)是一种循环机制,负责监听调用栈和消息队列,当调用栈中的代码执行完毕,EvenLoop就会从消息队列中按顺序读取任务并执行 - 消息队列 是用来存放宏任务的队列,例如定时器和回调函数 - 宏任务 宏任务可以理解为每次调用栈执行的代码就是一个宏任务(包括每次从消息队列中获取一个回调函数并放到调用栈中执行)。 浏览器为了让 JS 内部宏任务 与 DOM 操作能够有序的执行,会在一个宏任务执行结束后,在下一个宏任务执行开始前,对页面进行重新渲染。 宏任务包含: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) }, 10) }, 10) },10) ``` ##### >>>> ``` new Promise((resolve, reject) => { var a = 'hello'; resolve(a); }).then(value => { var b = 'lagou'; return value + b; }).then(value => { var c = 'I ❤ U'; console.log(value + c); }) ``` #### 二、基于以下代码完成下面的四个练习 ``` 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: 'Jaquar 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) } ``` > 先获取最后一条数据,再获取这条数据的"in_stock"属性的值,用flowRight组合函数从右到左执行 ``` let isLastInStock = fp.flowRight(fp.prop('in_stock'), fp.last); console.log(isLastInStock(cars)); // false ``` ##### 练习2:使用fp.flowRight()、fp.prop()和fp.first()获取第一个car的name >先获取最后一条数据,再获取这条数据的"name"属性的值,用flowRight组合函数从右到左执行 ``` let isFirstName = fp.flowRight(fp.prop('name'), fp.first); console.log(isFirstName(cars)); // Ferrari FF ``` ##### 练习3:使用帮助函数 _average重构averageDollarValue,使用函数组合的方式实现 ``` let _average = function (xs) { return fp.reduce(fp.add, 0, xs) / xs.length } // <- 无需改动 let averageDollarValue = function (cars) { let dollar_value = fp.map(function (car) { return car.dollar_value }, cars) return _average(dollar_value) } ``` > 先遍历数组并返回一个"dollar_value"属性的数组,然后用_average函数取平均值 ``` let averageDollarValue = fp.flowRight(_average, fp.map(car => car.dollar_value)); console.log(averageDollarValue (cars)); //790700 ``` ##### 练习4:使用flowRight 写一个sanitizeNames() 函数,返回一个下划线连接的小写字符串,把数组中的name转换为这种形式:例如:sanitizeNames(["Hello World"]) => ["hello_world"] ``` let _underScore = fp.replace(/\W+/g,'_') // <-- 无须改动,并在 sanitizeNames中使用它 ``` > 先组合_underScore函数和fp.toLower函数,再用fp.map方法把遍历的cars里的name传给组合函数并返回一个目标数组 ``` let sanitizeNames = fp.map(car => fp.flowRight(fp.toLower,_underScore)(car.name)); console.log(sanitizeNames(cars)); // [ // 'ferrari_ff', // 'spyker_c12_zagato', // 'jaquar_xkr_s', // 'audi_r8', // 'aston_martin_one_77', // 'pagani_huayra' // ] ``` #### 三、基于下面提供的代码,完成后续的四个练习 ``` // 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) 创建一个能让function 里的值增加的函数 exl ``` // app.js const fp = require('lodash/fp') const { Maybe, Container } = require('./support') let maybe = Maybe.of([5,6,1]) let exl = () => { // 你需要实现的函数... } ``` > exl函数返回一个maybe.map方法,maybe.map方法传入一个fp.map函数遍历of中的数组并通过fp.add(1)给数组的值加1 ``` const fp = require('lodash/fp') const { Maybe, Container } = require('./support'); let maybe = Maybe.of([5,6,1]) let exl = () => { return maybe.map(fp.map(fp.add(1))) } console.log(exl()) // Maybe { _value: [ 6, 7, 2 ] } ``` ##### 练习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', 'si', 'do']) let ex2 = () => { // 你需要实现的函数... } ``` > exl函数返回一个xs.map方法,传入of中的数组,通过fp.first方法获取第一个元素 ``` let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'si', 'do']) let ex2 = () => { // 你需要实现的函数... return xs.map(fp.first) } console.log(ex2()); // Container { _value: 'do' } ``` ##### 练习3:实现一个函数exe3,使用 safeProp 和 fp.first 找到 user 的名字的首字母 ``` // app.js const fp = require('lodash/fp') const { Maybe, Container } = require('./support') let safePop = fp.curry(function (x, o) { return Maybe.of(0[x]) }) let user = { id: 2, name: 'Albert' } let ex3 = () => { // 你需要实现的函数... } ``` > safePop函数返回的是一个Maybe含子,直接链式调用里面的map方法,通过fp.first获取name属性值的首字母,再把结果返回 ``` let ex3 = val => { return safePop("name", val).map(fp.first) } console.log(ex3(user)) // Maybe { _value: 'A' } ``` ##### 练习4:使用Mayge重写ex4,不要有if语句 ``` // app.js const fp = require('lodash/fp') const { Maybe, Container } = require('./support') let ex4 = function (n) { if (n) { return parseInt(n) } } ``` > 直接用parseInt进行转换,Maybe含子防止空值异常 ``` let ex4 = n => Maybe.of(n).map(parseInt) console.log(ex4('199')) //Maybe { _value: 199 } console.log(ex4(null)) // Maybe { _value: null } ``` #### 四、手写实现MyPromise源码 要求:尽可能还原Promise中每一个API,并通过注释的方式描述思路和原理。 ``` const PENDING = 'pending'; // 等待 const FULFILLED = 'fulfilled'; // 成功 const REJECTED = 'rejected'; // 失败 class MyPromise { constructor (executor) { try{ executor(this.resolve, this.reject); } catch (e) { this.reject(e); } } // promise 状态 status = PENDING; // 成功之后的值 value = undefined; // 失败后的原因 reason = undefined; // 成功回调 successCallback = []; // 失败回调 failCallback = []; resolve = value => { // 如果状态不是等待 阻止程序向下执行 if(this.status !== PENDING) return; // 将状态更改为成功 this.status = FULFILLED; // 保存成功之后的值 this.value = value; // 判断成功回调是否存在 如果存在 调用 // this.successCallback && this.successCallback(this.value); while(this.successCallback.length) this.successCallback.shift()() } reject = reason => { // 如果状态不是等待 阻止程序向下执行 if(this.status !== PENDING) return; // 将状态更改为失败 this.status = REJECTED; // 保存失败后的原因 this.reason = reason; // 判断失败回调是否存在 如果存在 调用 // this.failCallback && this.failCallback(this.reason); while(this.failCallback.length) 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 === FULFILLED) { setTimeout(() => { try { let x = successCallback(this.value) resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }else if (this.status === REJECTED) { setTimeout(() => { try { let x = failCallback(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }else { // 等待 // 将成功回调和失败回调存储起来 this.successCallback.push(() => { setTimeout(() => { try { let x = successCallback(this.value) resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }); this.failCallback.push(() => { setTimeout(() => { try { let x = failCallback(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 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 = []; let index = 0; return new MyPromise( (resolve, reject) => { function addData(key, value) { result[key] = value; index++; if(index === 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, array[i]); } } }) } static resolve (value) { if(value instanceof MyPromise) return value; return new MyPromise(resolve => resolve(value)) } } function resolvePromise (promise2, x, resolve, reject) { // 判断是否自己返回自己 防止回调地狱 if(x === promise2){ return reject(new TypeError('Chaining cycle detected for promise #')) } if(x instanceof MyPromise) { // promise 对象 x.then(resolve, reject); } else { // 普通值 resolve(x); } } ```