阅读 519

Promise.all()实现根据传入url顺序, 返回按序结果

问题描述:

  • 网络请求是异步的, 如果使用for循环, 进行好多的数据请求, 返回的数据, 很可能不是按照用户输入的顺序返回的.

解决方法:

  • 这里可以使用Promise.all()方法实现, 异步网络请求, 返回的数据也是按序的.

export default functionlaodFiles(dataFiles) {
    return new Promise(((resolve, reject) => {
    // promise.all保证了, 输出的顺序和输入的是一致的
        Promise.all(dataFiles.map(dataFile => {
            axios.get(dataFile.url).then(res => ({date: new Date(res), data: res}))
        })).then(data => {
            resolve(data)
        }).catch(err => {
            reject(err)
        })
    }))
}

复制代码

实现原理

  • Promise.all内部是怎么实现按序的呢, 下面我们就来扒一扒Promise.all内部的秘密
  • 首先贴上, 手写的Promise.all()

正确写法:


function PromiseAll(promises) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(promises)) {
            return reject(new Error('传入的参数必须得是数组格式'))
        }
        let res = []
        let count;
        promises.forEach((promise, index) => {
        	// 这里其实需要判断一下, 当前遍历的promise是否是Promise类型, 但是, 这里没有判断, 想想是因为什么原因. 
            Promise.resolve(promise).then(data => {
            	// 注意点1: index用来保证按序存储
                res[index]  = data
                count++
                // 注意点2: count用来保证获取到了想要的所有数据
                if(count === promises.length) {
                    resolve(res)
                }
            }, err => {
            }).catch(err => {
                return reject(err)
            })
        })
    })
}

复制代码

不正确的写法一:

  • 上面代码的forEach方法中, 第一个参数promise遍历的当前对象, 后一个index是关键, 可能会有人在处理结果的时候, 像下面这样书写:

let count =0
let res = []
let length= promises.length
promises.forEach(promise => {
    Promise.resolve(promise).then(data => {
        res.push(data)
        // 直接判断判断当前的结果数组长度是否是传入的数组长度
        if(res.length === length) resolve(res)
    })
})

复制代码
  • 但是像这样直接用返回结果长度会有一个问题, 那就是, 里面的结果数据不是按序的, 只要结果返回就往里面push,可能第一个返回的结果, 并不是第一个传入参数对应的, 可能是第三个或者任何一个.

不正确的写法二:

  • 上面代码的forEach方法中, 第一个参数promise遍历的当前对象, 后一个index是关键, 那你说index是关键, 那我就带上就好了 , 然后,可能会有人在处理结果的时候, 像下面这样书写:

let count =0
let res = []
let length= promises.length
promises.forEach((promise, index) => {
    Promise.resolve(promise).then(data => {
    	// 1.根据返回的结果, 将数据按照对应的index放进数组, 就可以保证按序了
        res[index] = data
        // 2.直接判断判断当前的结果数组长度是否是传入的数组长度(这里有坑
        if(res.length === length) resolve(res)
    })
})

复制代码
  • 那你可能会说, 我带了inded也按序了, 我这样写就没问题了吧! 但是! 你想想看, 如果, 第一个返回的就是, 最后一个index, 那直接把数组装满了, 结果为: [undefined,undefined,undefined,undefined,......, data]. 这样肯定是错误的, 那之前的数据都是空的了.

总结

  • 在使用前人, 前辈创造出来的好工具的时候, 也想想里面的原理, 这样, 才能从前辈那里学到一些知识和技能.
  • 这里需要注意的是两个点:
  1. 需要对遍历的当前对象判断是否为Promise类型, 但是, 可以使用Promise.resolve()这样可以保证当前的对象是Promise类型.
  2. 需要使用index来进行返回数据的顺序问题. res[index] = data
  3. 需要定一个count用来判断是否将所有的数据获取完毕了.
文章分类
前端
文章标签