# 1. 实现一个带并发限制的异步调度器 Scheduler

实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个。完善下面代码中的 Scheduler 类,使得以下程序能正确输出。

class Scheduler {
  add(promiseCreator) { ... }
  // ...
}

const timeout = (time) => new Promise(resolve => {
  setTimeout(resolve, time)
})

const scheduler = new Scheduler()
const addTask = (time, order) => {
  scheduler.add(() => timeout(time)).then(() => console.log(order))
}

addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')
class Scheduler{
  constructor() {
      this.pendingState = []
      this.doingJobs = 0
  }

  add = (promiseCreator) => {
    return new Promise((resolve, reject) => {
      // 关键是给传过来的函数加个回调属性,当resolved的时候,就能返回对应的结果了。
      promiseCreator.resolve = resolve
      promiseCreator.reject = reject
      this.pendingState.push(promiseCreator)
      this.doJob()
    })
    
  }

  doJob = () => {

    if (this.doingJobs < 2) {
      if (this.pendingState.length) {
        this.doingJobs += 1
        const job = this.pendingState.shift()

        job().then(res => {
          job.resolve(res)
        }).catch(e => {
          job.reject(e)
        }).finally(() => {
          this.doingJobs -= 1
          this.doJob()
        })

      }
    }
  }
}

# 2. 请实现一个cacheRequest方法

请实现一个cacheRequest方法,保证当使用ajax(请求相同资源时,此题中相同资源的判断是以url为判断依据),真实网络层中,实际只发出一次请求(假设已存在request方法用于封装ajax请求,调用格式为:request(url, successCallback, failCallback)

比如调用方代码(并行请求)如下

cacheRequest('/user', data => {
  console.log('我是从A中请求的user,数据为' + data);
})

cacheRequest('/user', data => {
  console.log('我是从B中请求的user,数据为' + data);
})
const pendingUrlList = {}
const ajaxRes = {}


function catchRequest(url, callback) {
  if (ajaxRes[url]) {
      callback(ajaxRes[url])
      return
  }
  if (pendingUrlList[url]) {
      pendingUrlList[url].push(callback)
      return 
  } else {
      pendingUrlList[url] = [callback]
  }

  request(url, (res) => {
      pendingUrlList[url].map(cb => cb(res))
      ajaxRes[url] = res
      delete pendingUrlList(url)
    }, () => {
      delete pendingUrlList(url)
    })
   
}

# 3. 说出 child 的盒子模型

<style>
    .parent {
      background: red;
      width: 100px;
      height: 100px;
      padding: 0;
      margin: 0;
    }

    .child {
      background: green;
      width: 50px;
      height: 50px;
      padding: 10%;
      border: 10px solid black;
      box-sizing: border-box;
    }
</style>
<div class="parent">
   <div class="child"></div>
</div>

padding百分比特点: padding的百分比是相对于父元素宽度,如果父元素有宽度,相对于父元素宽度,如果没有,找其父辈元素的宽度,均没设宽度时,相对于屏幕的宽度。

padding是相对最近父级元素的,也就是100*10% = 10px,而不是相对自身的width,那么contentwidth就是50 - 10 * 2 - 10 * 2 = 10px

# 4. 说出执行结果

Function.prototype.a = () => alert(1);
Object.prototype.b = () => alert(2);
function A() {}
const a = new A();

a.a();

a.b();

答案: a.b()可以正常执行,a.a()报错

# 5. 写一个通用算法将该数组结构转换为以下菜单结构

let menu = [
    { "Id": 1, "ParentId": null, "Sort": 0, "Name": "菜单1" },
    { "Id": 2, "ParentId": 1,    "Sort": 0, "Name": "菜单1-1"},
    { "Id": 3, "ParentId": 1,    "Sort": 1, "Name": "菜单1-2"},
    { "Id": 4, "ParentId": 2,    "Sort": 2, "Name": "菜单1-1-2"},
    { "Id": 5, "ParentId": 2,    "Sort": 1, "Name": "菜单1-1-1"},
    { "Id": 6, "ParentId": null, "Sort": 1, "Name": "菜单2" },
    { "Id": 7, "ParentId": 6,    "Sort": 0, "Name": "菜单2-1"},
    { "Id": 8, "ParentId": 6,    "Sort": 1, "Name": "菜单2-2"},
    { "Id": 9, "ParentId": 8,    "Sort": 2, "Name": "菜单2-2-2"},
    { "Id": 10, "ParentId": 8,   "Sort": 1, "Name": "菜单2-2-1"},
    { "Id": 11, "ParentId": 10,  "Sort": 0, "Name": "菜单2-2-1-1"}
]
<ul>
  <li>
      <a>菜单1<a>
      <ul>
        // 子菜单节点
    </ul>
  </li>
  // 其他节点
</ul>
function getTotal(menu) {
  return menu.reduce((acc, item) => {
    if (item.ParentId) {
      helper(acc, item)
    } else{
      acc.push(item)
    }
    return acc
  }, [])
}

function helper(acc, item) {
  if (!acc) return
  for (let obj of acc) {
    if (obj.Id === item.ParentId) {
      obj.children = obj.children || []
      obj.children.push(item)
      obj.children.sort((a, b) => a.Sort - b.Sort)
    }
    helper(obj.children, item)
  }
}

function generateMenuHtml() {
  const newData = getTotal(menu)
  const res =  newData.map(item => getItemHtml(item)).join('')
  return res
}

function getItemHtml(item) {
  return `
    <ul>
     <li>
       <a>${item.Name}</a>
       ${item.children ? item.children.map(it => getItemHtml(it)).join('') : ''}
     </li>
    </ul>
  `
}

generateMenuHtml(menu)

# 6. 判断数组是否可以分为和相等的三部分

Consider an array A of n integers. Implement one function "split" to determine if array A can be split into three consecutive parts such that sum of each part is equal. If yes return true, otherwise return false;

Examples:

example 1: • input:[0,2,1,-6,6,-7,9,1,2,0,1] • output:true • explanation:0 + 2 + 1 = -6 + 6 - 7 + 9 + 1 = 2 + 0 + 1

function split(arr) {
  // implement
  const len = arr.length
  if (len < 3) return false

  let sum = 0

  for (let item of arr) {
    sum += item
  }
  
  if (sum % 3) {
    return false
  }

  let s = 0
  let flag = 0
  for (let item of arr) {
    s += item
    if (s == sum / 3) {
      flag += 1
      s = 0
    }
  }
  if (flag && flag % 3 === 0) return true
  return false
}

# 7. 实现重复函数

function repeat(times, mills, func) {

}

// example:
let log3 = repeat(3, 1000, console.log);
log3('hello', 'world');
log3('hello', 'world', '2');
// 1000ms后    
// 输出hello world
// 输出hello world 2

// 又过1000ms后
// 输出hello world
// 输出hello world 2

// 又过1000ms后
// 输出hello world
// 输出hello world 2
function repeat(times, mills, func) {
  return function () {
    const args = [...arguments]
    const _this = this
    let _times = times

    const timer = setInterval = (() => {
      
      if (_times > 0) {
        func.apply(_this, args)
        _times -= 1
      } else {
        clearInterval(timer)
      }

    }, mills)
  }
}