HardenSG / blog

随笔录

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

资源预解析,写一个?

HardenSG opened this issue · comments

commented

当项目中有很多额外的静态资源时候,比如样式表比如图片等。如果将加载的时间交由触发时操作,那么用户就可能白白的等着资源加载出现的转圈圈或者白屏,这显然不是一个成熟的项目该有的样子。
对于项目中可能会使用到的资源,在白屏阶段或者loading阶段就进行预先加载,这在后续的使用时会大大提升响应速度!
为什么会提升响应速度呢?
因为对于这类静态资源来讲,往往都是可以被缓存的,所以也就是说预加载的真正目的其实就是为了触发浏览器的强缓存,这样在后续加载资源的时候就可以直接命中缓存,就不需要发起那该死的网络请求了。
这才是预加载的真正目的!

const ERROR_MSG = {
    'ERROR': 'REQUEST_FAIL',
    'REDIRECTION': 'REQUEST_REDIRECTION_LOCATION',
    'OVERTIME': 'OVER_MAX_RETRY_TIME'
}

const axios = (url, resolve, reject) => {
    https.get(url, res => {
        res.on('end', () => {
            const status = res.statusCode
            if(status > 300 && status < 400) {
                const location = res.getResponseHeader('Location')
                reject(ERROR_MSG.REDIRECTION, location)
            }
            resolve()
        })
    }).on('error', (err) => {
        reject(ERROR_MSG.ERROR)
    })
}

class Scheduler {
    constructor(items, options) {
        this.options = options
        this.reqPools = []  
        this.Init(items)
    }

    Init(items) {
        this.handleOptions()
        if(!Array.isArray(items)) {
            throw new Error("NOT A ARRAY FOR THIS VARIABLE")
        }
        items.forEach(v => {
            this.reqPools.push(new SyncPoolItem(v, this.isRetry, this.maxTryTime))
        })
    }

    handleOptions() {
        for (const k in this.options) {
            if (Object.hasOwnProperty.call(this.options, k)) {
                const v = this.options[k];
                this[k] = v
            }
        }
    }

    run(resolve, rej) {
        const maxSync = this['maxSync'] || 5

        // Init Container
        const container = this.reqPools.splice(0, maxSync)
        container.map((v, i) => 
            v.run().then(() => {
                return i
            }).catch(err => {
                throw new Error(err)
            })
        )

        // 遍历列表
        return new Promise(() => {
            this.reqPools.reduce((c, r) => {
                return c
                .then(() => {
                    return Promise.race(container)
                }).then(i => {
                    container[i] = r.run().then(res => {
                        return i
                    })
                }).catch(err => {
                    rej(err)
                })
            }, Promise.resolve())
            .then(res => {
                Promise.all(container).then(res => {
                    resolve()
                }).catch(err => {
                    rej(err)
                })
            })
        })

    }
}

class SyncPoolItem {
    constructor(url, isRetry, maxTryTime) {
        this.url = url
        this.isRetry = isRetry
        this.maxTryTime = maxTryTime
        this.currentRetryTime = 1
    }

    // retry's core
    async run() {
        // console.log(this.isRetry, this.currentRetryTime, this.maxTryTime, this.url);
        if(!this.isRetry || this.currentRetryTime >= this.maxTryTime) {
            throw new Error(ERROR_MSG.OVERTIME)
        }

        try {
            return await new Promise((resolve, reject) => {
                axios(this.url, resolve, reject)
            })
        } catch (err) {
            this.currentRetryTime++
            this.run()
        }
    }
}


/**
 *  @param urls: string[]
 */
function preload(urls) {
    const reqPools = urls.map(v => v)

    return async (options) => {
        const scheduler = new Scheduler(reqPools, options)
        try {
            const res = await new Promise((resolve, reject) => {
                scheduler.run(resolve, reject)
                console.log(2)
            })
            console.log('res', res)
        } catch (err) {
            console.log('err', err)
        }
    }
}


preload(urls)({
    maxSync: 5,
    isRetry: true,
    maxTryTime: 3
}).then(res => {
    console.log('res_', res);
}).catch(err => {
    console.log('err_', err);
})