Optimize loading with multi-threaded worker loader
This commit is contained in:
26
public/src/js/loader-worker.js
Normal file
26
public/src/js/loader-worker.js
Normal file
@@ -0,0 +1,26 @@
|
||||
self.addEventListener('message', async e => {
|
||||
const { id, url, type } = e.data
|
||||
try{
|
||||
const response = await fetch(url)
|
||||
if(!response.ok){
|
||||
throw new Error(response.status + " " + response.statusText)
|
||||
}
|
||||
let data
|
||||
if(type === "arraybuffer"){
|
||||
data = await response.arrayBuffer()
|
||||
}else if(type === "blob"){
|
||||
data = await response.blob()
|
||||
}else{
|
||||
data = await response.text()
|
||||
}
|
||||
self.postMessage({
|
||||
id: id,
|
||||
data: data
|
||||
}, type === "arraybuffer" ? [data] : undefined)
|
||||
}catch(e){
|
||||
self.postMessage({
|
||||
id: id,
|
||||
error: e.toString()
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -11,6 +11,8 @@ class Loader{
|
||||
this.errorMessages = []
|
||||
this.songSearchGradient = "linear-gradient(to top, rgba(245, 246, 252, 0.08), #ff5963), "
|
||||
|
||||
this.initWorkers()
|
||||
|
||||
var promises = []
|
||||
|
||||
promises.push(this.ajax("src/views/loader.html").then(page => {
|
||||
@@ -23,6 +25,78 @@ class Loader{
|
||||
|
||||
Promise.all(promises).then(this.run.bind(this))
|
||||
}
|
||||
initWorkers(){
|
||||
this.workers = []
|
||||
this.workerQueue = []
|
||||
this.workerCallbacks = {}
|
||||
this.workerId = 0
|
||||
var concurrency = navigator.hardwareConcurrency || 4
|
||||
for(var i = 0; i < concurrency; i++){
|
||||
var worker = new Worker("src/js/loader-worker.js")
|
||||
worker.onmessage = this.onWorkerMessage.bind(this)
|
||||
this.workers.push({
|
||||
worker: worker,
|
||||
active: 0
|
||||
})
|
||||
}
|
||||
}
|
||||
onWorkerMessage(e){
|
||||
var data = e.data
|
||||
var callback = this.workerCallbacks[data.id]
|
||||
if(callback){
|
||||
delete this.workerCallbacks[data.id]
|
||||
if(data.error){
|
||||
callback.reject(data.error)
|
||||
}else{
|
||||
callback.resolve(data.data)
|
||||
}
|
||||
this.workerFree(callback.workerIndex)
|
||||
}
|
||||
}
|
||||
workerFetch(url, type){
|
||||
return new Promise((resolve, reject) => {
|
||||
var id = ++this.workerId
|
||||
this.workerQueue.push({
|
||||
id: id,
|
||||
url: new URL(url, location.href).href,
|
||||
type: type,
|
||||
resolve: resolve,
|
||||
reject: reject
|
||||
})
|
||||
this.workerRun()
|
||||
})
|
||||
}
|
||||
workerRun(){
|
||||
if(this.workerQueue.length === 0){
|
||||
return
|
||||
}
|
||||
var workerIndex = -1
|
||||
var minActive = Infinity
|
||||
for(var i = 0; i < this.workers.length; i++){
|
||||
if(this.workers[i].active < minActive){
|
||||
minActive = this.workers[i].active
|
||||
workerIndex = i
|
||||
}
|
||||
}
|
||||
if(workerIndex !== -1){
|
||||
var task = this.workerQueue.shift()
|
||||
var workerObj = this.workers[workerIndex]
|
||||
workerObj.active++
|
||||
this.workerCallbacks[task.id] = task
|
||||
task.workerIndex = workerIndex
|
||||
workerObj.worker.postMessage({
|
||||
id: task.id,
|
||||
url: task.url,
|
||||
type: task.type
|
||||
})
|
||||
}
|
||||
}
|
||||
workerFree(index){
|
||||
if(this.workers[index]){
|
||||
this.workers[index].active--
|
||||
this.workerRun()
|
||||
}
|
||||
}
|
||||
run(){
|
||||
this.promises = []
|
||||
this.loaderDiv = document.querySelector("#loader")
|
||||
@@ -538,6 +612,17 @@ class Loader{
|
||||
return css.join("\n")
|
||||
}
|
||||
ajax(url, customRequest, customResponse){
|
||||
if(!customResponse && (url.startsWith("src/") || url.startsWith("assets/") || url.indexOf("img/") !== -1 || url.indexOf("audio/") !== -1 || url.indexOf("fonts/") !== -1 || url.indexOf("views/") !== -1)){
|
||||
var type = "text"
|
||||
if(customRequest){
|
||||
var reqStub = {}
|
||||
customRequest(reqStub)
|
||||
if(reqStub.responseType){
|
||||
type = reqStub.responseType
|
||||
}
|
||||
}
|
||||
return this.workerFetch(url, type)
|
||||
}
|
||||
var request = new XMLHttpRequest()
|
||||
request.open("GET", url)
|
||||
var promise = pageEvents.load(request)
|
||||
@@ -557,12 +642,13 @@ class Loader{
|
||||
return promise
|
||||
}
|
||||
loadScript(url){
|
||||
var script = document.createElement("script")
|
||||
var url = url + this.queryString
|
||||
var promise = pageEvents.load(script)
|
||||
script.src = url
|
||||
document.head.appendChild(script)
|
||||
return promise
|
||||
return this.workerFetch(url, "text").then(code => {
|
||||
var script = document.createElement("script")
|
||||
code += "\n//# sourceURL=" + url
|
||||
script.text = code
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
}
|
||||
getCsrfToken(){
|
||||
return this.ajax("api/csrftoken").then(response => {
|
||||
|
||||
Reference in New Issue
Block a user