問題圖示如下
再進一步說明問題-
按鈕A按了之後,ajax請求的資料顯示在input type=text框裡,B按鈕也是。
-
問題就是如果先按A,此時ajax發出去了,但是資料還沒返回來, 我們等不及了,馬上按B按鈕,結果此時A按鈕請求的資料先回來,這就尷尬了,按的b按鈕,結果先顯示A按鈕返回的資料,怎麼解決?
這個問題的解釋在我之前寫的一篇文章,其中的第二題就是解決方https://juejin.im/post/5a1810b56fb9a0452405854c,我們今天把這個問題展開
一、js的非同步的執行機制是什麼
以下是一張解釋非同步佇列的圖,以及文字說明(摘自阮一峰老師的部落格)
)(1)所有同步任務都在主執行緒上執行,形成一個執行棧(execution context stack)。
(2)主執行緒之外,還存在一個"任務佇列"(task queue)。只要非同步任務有了執行結果,就在"任務佇列"之中放置一個事件。
(3)一旦"執行棧"中的所有同步任務執行完畢,系統就會讀取"任務佇列",看看裡面有哪些事件。那些對應的非同步任務,於是結束等待狀態,進入執行棧,開始執行。
(4)主執行緒不斷重複上面的第三步。
複製程式碼
這只是針對跟我一樣中級水平或偏下的人普及一下JS非同步的執行原理。之後,我們來看一個非同步引發的問題程式碼
var res = [];
function response(data) {
res.push( data );
}
// ajax(..)是某個庫中提供的某個Ajax函式
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );
複製程式碼
問題來了,我們假定期望的行為是res[0] 中放呼叫"http://some.url.1" 的結果,res[1] 中放呼叫"http://some.url.2" 的結果,改怎麼辦呢? 解決辦法如下
var res = [];
function response(data) {
if (data.url == "http://some.url.1") {
res[0] = data;
}
else if (data.url == "http://some.url.2") {
res[1] = data;
}
}
// ajax(..)是某個庫中提供的某個Ajax函式
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );
複製程式碼
上面這個場景用於多個併發函式共享DOM的問題,所以回撥函式可以改進成這樣的寫法
var res = [];
function response(data) {
if (data.url == "http://some.url.1") {
//執行操作的函式,把引數data傳入進去
callbackA(data)
}
else if (data.url == "http://some.url.2") {
//執行操作的函式,把引數data傳入進去
callbackB(data)
}
}
// ajax(..)是某個庫中提供的某個Ajax函式
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );
複製程式碼
好了,我們總結出一種處理併發共享DOM問題的解決方案,這個方案是我在一本叫《你不知道javascript 中卷》看到的,考官繼續說,不用這種方法,因為要依賴後端發的資料要包含data.url,也就是url這個屬性,怎樣不依賴後端,前端獨立解決這個問題呢? 在response上我們做一下改動,設定一個全域性變數
var status; //值是undefined
複製程式碼
我們在點選A按鈕的時候, 讓status的值變為A
status = "A";
複製程式碼
我們在點選B按鈕的時候,讓status的值變為B
status = "B";
複製程式碼
也就是resopnse改成這樣:
function response(data) {
var status;
if(status = "A") {
// 點選A按鈕status就變為“A”,所以不會執行按鈕B的回撥函式
執行 callbackA() A按鈕的回撥函式
}else if(status = "B"){
// 點選B按鈕status就變為“B”,所以不會執行按鈕A的回撥函式
執行 callbackB() B按鈕的回撥函式
}
}
複製程式碼
這樣就解決了點A只顯示A的資料,點B只顯示B的資料的問題。
在這裡我們繼續延伸這個話題, 請看以下場景
var a,b;
function foo(x) {
a = x * 2;
baz();
}
function bar(y) {
b = y * 2;
baz()
}
function baz() {
console.log(a+b)
}
// ajax(..)是某個庫中提供的某個Ajax函式
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );
複製程式碼
我們的目的是等a,b都非同步請求回來才執行baz,解決辦法如下
var a,b;
function foo(x) {
a = x * 2;
if(a && b) {
baz();
}
}
function bar(y) {
b = y * 2;
if(a && b) {
baz();
}
}
function baz() {
console.log(a+b)
}
// ajax(..)是某個庫中提供的某個Ajax函式
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );
複製程式碼
接著,我們再換一個場景, 請看以下程式碼
var a;
function foo(x) {
a = x * 2
baz();
}
function bar(x) {
a = x / 2;
baz();
}
function baz() {
console.log(a)
}
// ajax(..)是某個庫中提供的某個Ajax函式
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );
複製程式碼
a的值會改變兩次,需求是隻讓a變一次,也就是第一次改變,第二次就不改變了
var a;
function foo(x) {
if(!a) {
a = x * 2;
baz();
}
}
function bar(y) {
if(!a) {
a = x / 2;
baz();
}
}
function baz() {
console.log(a)
}
// ajax(..)是某個庫中提供的某個Ajax函式
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );
複製程式碼
好了,下面我要寫一篇用promise解決非同步問題的隨筆(曾經看了一篇關於原生實現promise的文章,到時候也會簡單介紹下一個簡單的,但不是完全體的promise實現程式碼,只是為了大家更容易理解promise實現的內部大致原理),題目如下,需要20張圖片,每次發出10個非同步請求,請求10張圖片,所以一共要請求兩次,而且要求每次請求的10張圖片是按順序接收的,比如第一次發的請求是請求趙麗穎的圖片,第二次發的請求是請求張三瘋的圖片,要求接收的順序也是趙麗穎圖片,張三瘋圖片 ...以此類推。