最近做微信小程序校園失物巡回箱的時候需要完成一個邏輯就是用循環while/for 來不斷向雲數據庫發送讀寫請求,有感而寫,希望能給你啟發。
問題闡述
關於不斷循環發送請求這一點,由於發送請求本是耗時操作,本意打算用promise來將異步變成同步,但試過之后發現循環只運行一次,原因尚不清楚。可能是由於js的線程堵塞機制引發的阻塞問題,
展示代碼
promse請求
故障:只能運行一次
onLoad:function(){
for (let i=0;i<9;i++){
return new Promise((resolve,reject) =>{
//執行代碼成功
resolve();
//執行代碼失敗
reject();
}).then(res =>{});
}
}
async await請求
解決只能與逆行一次的問題
async onLoad(){
for(let i=0;i<9;i++){
await new Promise((resolve,reject) =>{
//執行代碼成功
resolve();
//執行代碼失敗
reject();
}).then(res =>{});
}
}
js阻塞機制
阻塞機制解釋
JavaScript是單線程執行的,無法同時執行多段代碼。當某一段代碼正在執行的時候,所有后續的任務都必須等待,形成一個隊列。一旦當前任務執行完畢,再從隊列中取出下一個任務,這也常被稱為 “阻塞式執行”。所以一次鼠標點擊,或是計時器到達時間點,或是Ajax請求完成觸發了回調函數,這些事件處理程序或回調函數都不會立即運行,而是立即排隊,一旦線程有空閑就執行。
關鍵說明:假如當前 JavaScript線程正在執行一段很耗時的代碼,此時發生了一次鼠標點擊,那么事件處理程序就被阻塞,用戶也無法立即看到反饋,事件處理程序會被放入任務隊列,直到前面的代碼結束以后才會開始執行。如果代碼中設定了一個 setTimeout,那么瀏覽器便會在合適的時間,將代碼插入任務隊列,如果這個時間設為 0,就代表立即插入隊列,但不是立即執行,仍然要等待前面代碼執行完畢。所以 setTimeout 並不能保證執行的時間,是否及時執行取決於 JavaScript 線程是擁擠還是空閑。
js阻塞機制,跟Js引擎的單線程處理方式有關,每個window一個JS線程。所謂單線程,在某個特定的時刻只有特定的代碼能夠被執行,並阻塞其它的代碼。
以下給個示例:for(var i=0;i<3;i++){
setTimeout(function(){
console.log(i);
}, (i+1)*1000);
}
一般,我們會認為,這段代碼會log出來0,1,2.而實際上,這段代碼log出來的結果是 3,3,3。具體原因就是因為for循環的阻塞機制。在上面的代碼中,setTimeout這個定時器需要等待for循環 執行完成,而for循環執行完成了之后,i已經為3了,此時才開始執行setTimeout,因此console.log(i)會是3。至於為什么i會是3,當i為2的時候,滿足循環條件,執行代碼塊,然后i++,此時i為3,不滿足循環條件,不執行代碼塊,循環停止。
阻塞機制解決辦法
其實,阻塞作為js引擎的處理方式,我們最好不要想着解決“阻塞”,而是讓我們想執行的代碼,插入到“主線程”中。這么說比較不易理解,還是以上面的代碼為例,直接上代碼好了
for(var i=0;i<3;i++){
(function(i){
setTimeout(function(){
console.log(i);
}, (i+1)*1000);
})(i)
}
在上面的代碼中,我們加了一個立即執行的匿名函數,並且將for循環的i作為實參傳入進去。這樣,setTimeout就會被立即執行,而不會等待(這里不太了解細節,就不多說了,大概猜測為新開了一個臨時的線程,立即執行匿名函數,然后再立即切換回來)。
注意:順帶提一下,html5支持Web worker功能,可以寫多線程,小程序同樣可以用worker功能。
最后
本篇文章引用的文章鏈接如下,感謝他們的幫助:
JavaScript的單線程與阻塞式執行(附案例)