JS编程,函数,functor ,Promise手写源码
解答题
一. 谈谈你是如何理解JS异步编程的,EventLoop,消息队列都是做什么的,什么是宏任务,什么是微任务?
-
JS异步编程:
- 不会去等待这个任务的结束才开始下一个任务,
- 对于耗时操作,开启过后就立即往后执行下一个任务,
- 后续逻辑一般会通过回调函数的方式定义,
- 内部耗时任务执行过后,就会执行回调函数,
- 如果没有异步编程的话,单线程的JavaScript语言就无法同时处理大量耗时任务,
- 而单线程的难点就是,代码的顺序混乱
-
EventLoop:
-
负责监听调用栈和消息队列,
-
一旦调用栈中所有的任务都结束,
-
事件循环就会从消息队列中取出第一个回调函数,
-
然后压入到调用栈

-
消息队列:(查看以下图)
-
发起异步调用,
-
执行异步任务,
-
异步回调会依次放入消息队列,
-
JS主线程在完成所有的任务后,
-
会依次执行消息队列的任务

-
宏任务:
- 是消息队列里的任务,常见的接口请求、定时器等异步任务都是宏任务。
-
微任务:
- 是基于当前任务产生而随当前任务结束后立即执行的任务,所以也是异步任务, 但是不需要通过EventLoop监测,通过消息队列取出并压入执行栈中再执行; 像通过Promise、MutationObserver、process.nextTick产生的任务都为微任务。
代码题
一. 将下面异步代码使用Promise的方式改进
setTimeout(function () {
var a = 'hello'
setTimeout(function () {
var b = 'lagou'
setTimeout(function (params) {
var c = 'I 💗 U'
console.log(a + b + c)
}, 10);
}, 10);
}, 10);
答:Promise改进的方式
let a = 'hello'
let b = 'lagou'
let c = 'I 💗 U'
Promise.all([a, b, c]).then(value => {
const total = value.reduce((t, d) => t + d, '')
console.log(total)
})
二. 基于以下代码完成下面的四个练习
const fp = require('lodash/fp') 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: 'Andi R8', horsepower: 525, dollar_value: 1142000, in_stock: true }, { 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)
}
练习1改进:
let isLastInStock = fp.flowRight(fp.prop('in_stock'), fp.last)
练习2: 使用fp.flowRight(), fp.prop()和fp.first()获取第一个car的第一个name
let isLastInStock = fp.flowRight(fp.prop('name'), fp.first)
练习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) {
console.log(car.dollar_value)
return car.dollar_value
}, cars)
return _average(dollar_values)
}
练习3重构:
let dollar_values = cars => fp.map(car => car.dollar_value, cars) let averageDollarValue = fp.flowRight(_average, dollar_values) console.log(averageDollarValue(cars))
练习4: 使用flowRight写一个sanitizeName() 函数,返回一个下划线连接的小写字符串,把数组中的name转换为这种形式:例如:sanitizeNames(["Hello World"]) => ["hello_world"]
let _underscore = fp.replace(/\W+/g, '_') //<--无需改动,并sanitizeNames中使用它
练习4: 写函数
let fp = require('lodash/fp')
let _underscore = fp.replace(/\W+/g, '_') //<--无需改动,并在sanitizeNames中使用它
let sanitizeNames = fp.flowRight(fp.toLower, _underscore)
console.log(sanitizeNames(["Hello World"]))
三. 基于下面提供的代码,完成后续的四个练习
supper.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 = {
Container,
Maybe
}
练习1:使用 fp.add(x, y) 和 fp.map(f, x) 创建一个能让 functor 里的值增加的函数 ex1
const fp = require('lodash/fp')
const { Maybe, Container } = require('./5.support.js')
let maybe = Maybe.of([5, 6, 1])
let ex1 = maybe.map(x => fp.map(fp.add(1), x))
console.log(ex1)
练习2: 实现一个函数ex2,能够使用fp.first获取列表的第一个元素
let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do']) let ex2 = xs.map(fp.first)._value console.log(ex2)
练习3:实现一个函数 ex3,使用 safeProp 和 fp.first 找到 user 的名字的首字母
let safeProp = fp.curry(function (x, o) {
return Maybe.of(o[x])
})
let user = { id: 2, name: 'Albert' }
const value = fp.flowRight(fp.map(fp.first), safeProp('name'))
console.log(value(user))
练习4:使用 Maybe 重写 ex4,不要有 if 语句
let ex4 = (n) => Maybe.of(n).map(parseInt)
// console.log(ex4('1')) 1
// console.log(ex4(1)) 1
// console.log(ex4(undefined)) undefined
// console.log(ex4(null)) undefined
// console.log(ex4('null')) NaN
// console.log(ex4('Abc')) // NaN
四. 手写实现Promise源码
要求:尽可能还原Promise中的每一个API,并通过注释的方式描述思路和原理
/**
* Promise源码解析
*
* Promise 是一个类 == MyPromise
*
* 1.Promise 状态
* @param PENDING 等待中
* @param FULFILLED 成功
* @param RESOLVED 失败
* @param status 当前执行状态----默认【PENDING】
*
* 2.Promise 执行顺序
* pending -> fulfilled
* pending -> rejected
* 一旦状态确定就不可更改
*
* 3.Promise 变量
* @param value 成功返回结果
* @param reason 失败返回结果
*
* 4.Promise 方法
* @param exector 执行器
* @param resolve 调用成功方法(更改状态)
* @param reject 调用失败方法 (更改状态)
* @param then 执行成功方法
* @successCallBack 成功回调函数
* @errorCallBack 失败回调函数
* @param catch 执行失败方法
* @param finally 执行finally内部方法
*
* 5.Promise 静态方法
* @param resolve 成功方法
* @param all 可---数组异步---方法
*
*
* 6.自定义方法
* @param resolvePromise 1.判断普通值 OR Promise 对象,2.返回值和调用promise是否相同
*/
let PENDING = 'Pending'
let FULFILLED = 'Fulfilled'
let RESOLVED = 'Resolved'
class MyPromise {
constructor(exector) {
// 通过 new 命令创建对象实例时,自动调用--exector--执行器。
// 捕获错误
try {
exector(this.resolve, this.reject)
} catch (e) {
this.reject(e)
}
}
// 成功之后的值
value = undefined
// 失败之后的原因
reason = undefined
// promise 状态
status = PENDING
// 成功回调方法
successCallBack = []
// 失败回调方法
errorCallBack = []
resolve = value => {
// 如果执行后状态不为【等待中】则终止,并实现一旦状态改变,就不会再变
if (this.status !== PENDING) return
// 保存成功之后的值
this.value = value
// 将状态更改为成功
this.status = FULFILLED
// 判断成功回调是否存在 如果存在 调用
// this.successCallBack && this.successCallBack(value)
// 始终获得成功回调的第一个值
while (this.successCallBack.length) this.successCallBack.shift()()
}
reject = reason => {
// 如果执行后状态不为【等待中】则终止,并实现一旦状态改变,就不会再变
if (this.status !== PENDING) return
// 保存失败之后的原因
this.reason = reason
// 将状态更改为失败
this.status = RESOLVED
// 判断失败回调是否存在 如果存在 调用
// this.errorCallBack && this.errorCallBack(reason)
while (this.errorCallBack.length) this.errorCallBack.shift()()
}
then = (successCallBack, errorCallBack) => {
// 当 then 中不传入参数时,定义默认 value => value
successCallBack = successCallBack ? successCallBack : value => value
errorCallBack = errorCallBack ? errorCallBack : reason => { throw reason }
const promise = new MyPromise((resolve, reject) => {
// 如果执行后状态为【成功】执行 “successCallBack” 方法,否则执行 “errorCallBack”方法
if (this.status === FULFILLED) {
// 异步实现
setTimeout(() => {
// 捕获 then 中的错误
try {
// 成功回调返回的返回值
const x = successCallBack(this.value)
// 判断 x 的值是普通值 OR Promise对象
resolvePromise(x, promise, resolve, reject)
} catch (e) {
// 捕获到错误,直接 reject 将错误信息返回
reject(e)
}
}, 0);
} else if (this.status === RESOLVED) {
setTimeout(() => {
// 捕获 then 中的错误
try {
// 失败回调返回的返回值
const x = errorCallBack(this.reason)
// 判断 x 的值是普通值 OR Promise对象
resolvePromise(x, promise, resolve, reject)
} catch (e) {
// 捕获到错误,直接 reject 将错误信息返回
reject(e)
}
}, 0);
} else {
// 成功回调和失败回调存储
this.successCallBack.push(() => {
setTimeout(() => {
// 捕获 then 中的错误
try {
// 成功回调返回的返回值
const x = successCallBack(this.value)
// 判断 x 的值是普通值 OR Promise对象
resolvePromise(x, promise, resolve, reject)
} catch (e) {
// 捕获到错误,直接 reject 将错误信息返回
reject(e)
}
}, 0);
})
this.errorCallBack.push(() => {
setTimeout(() => {
// 捕获 then 中的错误
try {
// 失败回调返回的返回值
const x = errorCallBack(this.reason)
// 判断 x 的值是普通值 OR Promise对象
resolvePromise(x, promise, resolve, reject)
} catch (e) {
// 捕获到错误,直接 reject 将错误信息返回
reject(e)
}
}, 0);
})
}
})
return promise
}
// 失败方法
catch(failcallback) {
// 将 失败方法 【failcallback】直接放入then的第二个方法中
return this.then(undefined, failcallback)
}
// Promise: finally 方法
finally = (callback) => {
// 返回 Promise 对象
return this.then(value => {
return MyPromise.resolve(callback()).then(() => value)
}, reason => {
return MyPromise.resolve(callback()).then(() => { throw reason })
})
}
// 静态方法
static all(array) {
// 创建一个存储数组
let allArray = []
// 计算在数组中的哪个位置
let index = 0
// 返回 Promise 对象
return new MyPromise((resolve, reject) => {
// 在存储数组中新增对象
function addDate(key, value) {
allArray[key] = value
// 所在位置增加
index++
// 当所在位置与传入参数的数组长度相同,则将存储数组返回
if (index === array.length) {
resolve(allArray)
}
}
// 传入参数循环
for (let i = 0; i < array.length; i++) {
let current = array[i]
if (current instanceof MyPromise) {
// 如promise对象,当成功则将值放入 allArray 中,失败则直接返回失败(Promise.all中只要有一个方式失败,则直接中断)
current.then(value => addDate(i, value), reason => reject(reason))
} else {
// 如普通值
addDate(i, current)
}
}
})
}
static resolve(value) {
// 如 Promise 对象
if (value instanceof MyPromise) return value
// 如 普通值
return new MyPromise(resolve => resolve(value))
}
}
function resolvePromise(x, promise, resolve, reject) {
// 当调用
if (promise === x) reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
if (x instanceof MyPromise) {
// 根据Promise对象返回的结果,决定调用resolve OR reject
x.then(resolve, reject)
} else {
// 判断是普通值 直接调用 resolve
resolve(x)
}
}
module.exports = {
MyPromise
}

浙公网安备 33010602011771号