Optimize loading with multi-threaded worker loader

This commit is contained in:
2026-01-03 16:10:27 +08:00
parent 6d7be5c45c
commit 8d58bf683f
3 changed files with 137 additions and 5 deletions

View File

@@ -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 => {