JS编程,函数,functor ,Promise手写源码

解答题

一. 谈谈你是如何理解JS异步编程的,EventLoop,消息队列都是做什么的,什么是宏任务,什么是微任务?

  • JS异步编程:

  1. 不会去等待这个任务的结束才开始下一个任务,
  2. 对于耗时操作,开启过后就立即往后执行下一个任务,
  3. 后续逻辑一般会通过回调函数的方式定义,
  4. 内部耗时任务执行过后,就会执行回调函数,
  5. 如果没有异步编程的话,单线程的JavaScript语言就无法同时处理大量耗时任务,
  6. 而单线程的难点就是,代码的顺序混乱
  • EventLoop:

  1. 负责监听调用栈和消息队列,

  2. 一旦调用栈中所有的任务都结束,

  3. 事件循环就会从消息队列中取出第一个回调函数,

  4. 然后压入到调用栈

  • 消息队列:(查看以下图)

  1. 发起异步调用,

  2. 执行异步任务,

  3. 异步回调会依次放入消息队列,

  4. JS主线程在完成所有的任务后,

  5. 会依次执行消息队列的任务

  • 宏任务:

  1. 是消息队列里的任务,常见的接口请求、定时器等异步任务都是宏任务。
  • 微任务:

  1. 是基于当前任务产生而随当前任务结束后立即执行的任务,所以也是异步任务, 但是不需要通过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
}

  

 

posted @ 2020-07-29 19:26  小短腿奔跑吧  阅读(1)  评论(0)    收藏  举报