搜尋

首頁  >  問答  >  主體

javascript - js最好怎麼在兩個AJax非同步操作之後執行一個新的操作

今天碰到一個面試問題,就是如果頁面中有兩個非同步ajax的操作,因為不確定這兩個非同步操作的執行順序,怎麼在這兩個操作都執行完再執行一個新的操作,最好的方法是什麼?

我當時回答了方法一:巢狀兩個ajax,在第二個ajax的回傳函數中執行新的操作。面試官回覆:這種方法太矬了。

於是想了下回答方法二:透過定時器setTimeout監聽局部變量,確保兩個非同步操作執行完了再執行新操作。面試官回覆:這種方式表現不好,能不能想到一個簡單又更合理的方法。

當時思考未果
所以把這個問題放上來尋求最好的方法是什麼?歡迎討論指點

世界只因有你世界只因有你2727 天前1699

全部回覆(11)我來回復

  • 学习ing

    学习ing2017-07-05 10:56:02

    1.Promise 包裝非同步ajax操作,
    2.定義async 函數,
    3.用await等待promise資料非同步取得完成
    這一種方法簡潔高效,下面請看我專門給你寫的範例程式碼
    我懶得用ajax取得資料了,就用settimeout這個函數模擬取得資料吧,這個函數是異步的,原理效果一樣。

    //模拟ajax异步操作1
    function ajax1() {
        const p = new Promise((resolve, reject) => {
            setTimeout(function() {
                resolve('ajax 1 has be loaded!')
            }, 1000)
        })
        return p
    
    }
    //模拟ajax异步操作2
    function ajax2() {
        const p = new Promise((resolve, reject) => {
            setTimeout(function() {
                resolve('ajax 2 has be loaded!')
            }, 2000)
        })
        return p
    }
    //等待两个ajax异步操作执行完了后执行的方法
    const myFunction = async function() {
        const x = await ajax1()
        const y = await ajax2()
            //等待两个异步ajax请求同时执行完毕后打印出数据
        console.log(x, y)
    }
    myFunction()

    回覆
    0
  • 漂亮男人

    漂亮男人2017-07-05 10:56:02

    http://api.jquery.com/jQuery....

    回覆
    0
  • 为情所困

    为情所困2017-07-05 10:56:02

    目前瀏覽器環境中開箱即用的原生方法是 Promise.all。

    以呼叫我的地圖庫 Sinomap Demo 為例,這個頁面中為了載入一張地圖,需要多個同時發起但不能確保返回順序的請求:

    1. 中國地形資料

    2. 各省份數值 JSON 資料

    3. 多種圖表疊加時多種圖表存在多種 JSON 資料需透過不同資料介面回傳…

    解決方法直接在未包裝的 http://sinomap.ewind.us/demo/demo.js 中,範例:

    // 封装地形 GeoJSON 数据接口
    // 将每个数据接口封装为一个返回 Promise 的函数
    function getArea () {
      return new Promise((resolve, reject) => {
        fetch('./resources/china.json').then(resp =>
          resp.json().then(china => resolve(china))
        )
      })
    }
    
    // 封装分色地图数据接口
    function getPopulation () {
      return new Promise((resolve, reject) => {
        fetch('./resources/china-population.json').then(resp =>
          resp.json().then(data => resolve(data))
        )
      })
    }
    
    // 封装城市数据接口
    function getCity () {
      return new Promise((resolve, reject) => {
        fetch('./resources/city.json').then(resp =>
          resp.json().then(data => resolve(data))
        )
      })
    }
    
    // 使用 Promise.all 以在三个数据接口均异步成功后,执行回调逻辑
    Promise.all([getArea(), getPopulation(), getCity()]).then(values => {
      // 依次从返回的数据接口数组中获取不同接口数据
      let china = values[0]
      let population = values[1]
      let city = values[2]
      // 使用数据
      doWithData(china, population, city)
    })

    這樣透過 Promise 不僅實現了回呼邏輯的解耦,也實現了基礎的非同步流程控制。

    回覆
    0
  • 高洛峰

    高洛峰2017-07-05 10:56:02

    剛剛看到jquery 的when 方法,所以給你重寫了一個,不一定有jquery的那麼好,但至少能實現效果了,可以在控制台直接輸入下述代碼試試,我勒個去,寫了我整整半小時。 。

    function ajax(callback){
        callback = callback || function(){};
        var xhr = new XMLHttpRequest();
        xhr.open("get","");
        xhr.onload = function(res){ callback(res) };
        xhr.send(null); 
    }
    
    var when = (function(){
        var i = 0,
            len = 0,
            data = [];
        return function(array,callback){
            callback = callback || function(){};
           len = len || array.length;
            var fn = array.shift();
           
           fn(function(res){
                i++;
                data.push(res);
                if(i < len){
                    when(array,callback);
                } else {
                    callback(data);
                } 
           });   
        };
    })();
    
    when([ajax,ajax],function(data){
        console.log(data);
    });
    

    回覆
    0
  • 给我你的怀抱

    给我你的怀抱2017-07-05 10:56:02

    問下能不能用jQ,能用的話直接:

    $.when($.ajax("page1"), $.ajax("page2")).done(function(){});

    順帶給個$.when的文檔參考

    回覆
    0
  • 为情所困

    为情所困2017-07-05 10:56:02

    我覺得是Promise的方法 all還是什麼的

    回覆
    0
  • 学习ing

    学习ing2017-07-05 10:56:02

    你的問題是有三件事 a,b,c。 c要在a和b結束之後再執行。

    有很多方法: 例如你說的嵌套法 還有暴力監聽法

    這個問題我曾經考慮過,以下是我的解答。

    異步發射器

    用數組保存如何執行非同步操作

    注意裡面的函數都有個參數 commit ,它是一個函數 用來來回傳值。 當ajax成功的時候 把回傳值回傳進去就好

    // 两个异步操作  
    var todos = [
        function getUser(commit){ 
            setTimeout(() => {
                commit({  // 这里是异步结束的时候 利用 commit 把值回传 
                    name: 'eczn',
                    age: 20
                }, 233); 
            }); 
        },
        function getLoc(commit){
            setTimeout(() => {
                commit({
                    area: '某个地方'
                });
            }, 333); 
        }
    ]; 

    編寫發射器

    processors 是 todos 這樣的數據。 cb 是最終回調。

    function launcher(processors, cb){
        var o = {}; 
        var count = 0; 
        if (processors.length === 0) cb(o); 
    
        processors.forEach((func, idx) => {
            func(function commit(asyncVal){ // 这就是commit函数 
                // 把 asyncVal 的所有属性合并到 o 上 
                // ( 利用 Object.keys 获取对象全部属性名 )
                Object.keys(asyncVal).forEach(key => {
                    o[key] = asyncVal[key]; 
                }); 
                
                // 计数器自加 
                count++; 
                // 如果发射器全部发射完毕则调用回调函数 cb 并把 o 作为参数传递
                if (count === processors.length) cb(o); 
            }); 
        }); 
    }

    並發他們

    執行非同步發射器 並提供 最終回調

    launcher(todos, function(whereEczn){
        // todos 里面存放的异步操作的值由 commit 回调返回
        // 全部回调跑完的时候 就会执行当前这段函数 并把期望值返回
        console.log(whereEczn); 
    
        // 按顺序输出
        ['name', 'area'].forEach(key => {
            console.log(`${key}: ${whereEczn[key]}`); 
        }); 
    });

    Link

    https://eczn.coding.me/blog/%...

    回覆
    0
  • 世界只因有你

    世界只因有你2017-07-05 10:56:02

    可以定義個變數a=0,ajax請求成功後在回呼裡設定a++;
    然後在兩個回調中均判斷下a==2 執行操作函數

    回覆
    0
  • 天蓬老师

    天蓬老师2017-07-05 10:56:02

    設定兩個flag,然後兩個ajax呼叫同一個回調,在這個回呼中判斷兩個flag都為true才執行後續操作。

    回覆
    0
  • 漂亮男人

    漂亮男人2017-07-05 10:56:02

    把ajax寫在另一個ajax裡面再在回調那裡執行

    回覆
    0
  • 取消回覆