前端请求并发控制

2,893 阅读2分钟

前言

今天去一个公司面试前端的时候,遇到这样一个问题: 假设我们有一个接口,getJson('story.json'),可以请求一个故事的所有章节对应的url的列表(按章节顺序排序);而每一章节的内容,我们可以请求getJson(url)得到,如今等到我们的需求是:并发请求所有章节的内容,为了得到更好的用户体验,我们应该及时的按顺序显示章节内容,也就是说不能等到收到所有章节内容在显示,函数addTextTopage(chapter)可以展示一章内容,请根据以上需求把下面代码补充完整

getJson('story.json')
  .then(function (story) {
    addHtmlToPage(story.heading)
    const allUrls = story.chapterUrls
    // 请在此处补充代码
  })
  .then(function () {
    addTextToPage('All Done')
  })
  .catch(function (err) {
    // catch any error that happend along the way
    addTextToPage('Argh, broken' + err.message)
  })
  .then(function () {
    document.querySelector('.spinner').style.display = 'none'
  })

思路分析

  1. 通过批量并且当所有请求结束后,在执行 callback 我们初步确定使用 Promise.all 可以实现此功能,
  2. 由于请求地址在 urls 数组中,因为是一个并行的问题,所以我们可以将请求地址进行按照 max 来进行分组,最后得到多少组就执行多少次请求

题解

getJson('story.json')
  .then(function (story) {
    addHtmlToPage(story.heading)
    const allUrls = story.chapterUrls
    // 请在此处补充代码
    
    // 先定义一个分组函数
    let max = 4
    function group(allUrls, max) {
    	let groupObj = {}
        allUrls.forEach((item, index) => {
        // 进行分组
        let group = parseInt(index / max)
        // 判断groupObj[group]是否为true,不为true就把item变为一个数组给groupObj[group],然后同一个group的其他item在为true的情况下都push进行
        if (groupObj[group]) {
            return groupObj[group].push(item)
        }
        groupObj[group] = [item]
        })
        return groupObj
    }
    function sendRequest(allUrls, max,callback) {
    	// 得到分组
        let groupResult = group(allUrls, max)
        let currentIndex = 0
        function getData(source) {
        	return source.map( url => getJson(url))
        }
        function send() {
        	// 判断当前有没有数组
            gruopResult[currentIndex] && Promise.all(getData(gruopResult[currentIndex]))
            .then (
            	(data)=>{
                	data.forEach((chapter) => {
                    	callback(chapter)
                    })
                 })
                 currentIndex++
                 // 递归调用,这个很重要
                 send()
        }
        send()
    }
    sendRequest(urls, max, addTextToPage)
  })
  .then(function () {
    addTextToPage('All Done')
  })
  .catch(function (err) {
    // catch any error that happend along the way
    addTextToPage('Argh, broken' + err.message)
  })
  .then(function () {
    document.querySelector('.spinner').style.display = 'none'
  })

参考1
参考2