由一道JS異步面試題,思考對異步問題的處理(1,暫時不用promise)

問題圖示以下javascript

再進一步說明問題

  • 按鈕A按了以後,ajax請求的數據顯示在input type=text框裏,B按鈕也是。前端

  • 問題就是若是先按A,此時ajax發出去了,可是數據還沒返回來, 咱們等不及了,立刻按B按鈕,結果此時A按鈕請求的數據先回來,這就尷尬了,按的b按鈕,結果先顯示A按鈕返回的數據,怎麼解決?java

這個問題的解釋在我以前寫的一篇文章,其中的第二題就是解決方https://juejin.im/post/5a1810b56fb9a0452405854c,咱們今天把這個問題展開ajax

1、js的異步的運行機制是什麼

如下是一張解釋異步隊列的圖,以及文字說明(摘自阮一峯老師的博客)後端

)

(1)全部同步任務都在主線程上執行,造成一個執行棧(execution context stack)。
(2)主線程以外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。
(3)一旦"執行棧"中的全部同步任務執行完畢,系統就會讀取"任務隊列",看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。
(4)主線程不斷重複上面的第三步。
複製代碼

這只是針對跟我同樣中級水平或偏下的人普及一下JS異步的運行原理。以後,咱們來看一個異步引起的問題代碼promise

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" 的結果,改怎麼辦呢? 解決辦法以下bash

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張圖片是按順序接收的,好比第一次發的請求是請求趙麗穎的圖片,第二次發的請求是請求張三瘋的圖片,要求接收的順序也是趙麗穎圖片,張三瘋圖片 ...以此類推。

相關文章
相關標籤/搜索