2016-07-13 1 views
1

サーバーからJSONファイルを一度ロードして、後でそのサイトでコンテンツを入力する必要があります。 XMLHttpRequestのJSONファイルを読み込むのは非同期で、できるだけ早く行う必要があります。しかし、コンテンツを取り込むには、DOMをロードする必要があります。非同期XMLHttpRequestのデータが利用可能になった後にコードを実行する

現在、私はxhr.open('GET', 'records.json', false)を呼び出して要求を同期させています。これは、JSONを読み込んで解析するのにブラウザがDOMをロードするのに要した時間よりも時間がかかる場合に必要でした。これが起こったとき(すなわち、接続が遅い場合)、DOMがロードされ、DOMContentLoadedイベントリスナーのコードはであるlistDataで実行されます。悪い。

非同期XMLHttpRequestを使用すると同時に、DOMContentLoadedでコードを実行するにはどうすればよいですか? listDataが完全に読み込まれると(つまりJSON.parse()が完了した)、DOMが利用可能になるとすぐにコードを実行したいのですが、両方の条件が満たされたら、私は行けます。

function thisFunctionIsCalledFromTheHtml() { 
    // Get the JSON data by using a XML http request 
    var listData; 
    var xhr = new XMLHttpRequest(); 
    // The request needs to be synchronous for now because on slow connections the DOM is ready 
    // before it fetches everything from the json file 
    xhr.open('GET', 'records.json', false); 
    xhr.addEventListener("load", function() { 
     if (xhr.readyState === 4) { 
      if (xhr.status === 200) { 
       listData = JSON.parse(xhr.responseText); 
      } else { 
       console.error('Error: ' + xhr.status + ' ' + xhr.statusText); 
      } 
     } 
    }); 
    xhr.addEventListener("error", function() { 
     console.error('Error: ' + xhr.status + ' ' + xhr.statusText); 
    }); 
    xhr.send(null); 

    document.addEventListener("DOMContentLoaded", function() { 
     var placeholderKeys = []; 
     for (var key in listData) { 
      var value = listData[key]; 
      placeholderKeys = placeholderKeys.concat(value.title, value.abbr, value.keywords); 
     } 

     var filterInput = document.getElementById('input-id'); 
     filterInput.placeholder = placeholderKeys[ 
      Math.floor(Math.random() * placeholderKeys.length) 
     ]; 
    }); 
} 
+1

'DOMContentLoaded'は必要ありませんあなたはこの関数は要素 – charlietfl

+0

@charlietfl後に置かれたスクリプトタグから呼び出されます。しかし、私は後までJSONを読み込むと待ちたくないことを確認した場合DOMがロードされました。できるだけ早くそれが起こるはずです。 – kleinfreund

+0

代わりに約束を返し、約束コールバックを 'DOMContentLoaded'に入れます。 – charlietfl

答えて

1

非同期取得要求と共に約束を使用し、成功した返されたデータをDOMContentLoadedコールバック内で利用できるものにアタッシュすることができます。この場合、データが割り当てられているかどうかを繰り返し確認できます。

let myData; 
 

 
let doAsyncOperation = (param) => { 
 
    if (window.Promise) { 
 
    let promise = new Promise((resolve, reject) => { 
 

 
     // Do your async request here... 
 
     window.setTimeout(() => { 
 
     // resolve the promise with successfully returned data from get req. 
 
     resolve(`Hello ${param}`) 
 
     }, 1000); 
 

 
     // reject the promise if your request returns an error 
 
     if (typeof param !== "string") reject("param must be a string"); 
 

 
    }) 
 
    return promise; 
 
    } else { 
 
    \t console.log("Sorry no promise for you, use a fallback ") 
 
    } 
 
} 
 

 
doAsyncOperation("Foo !").then(
 
    // on success do something with your data. 
 
    (data) => myData = data, 
 
    (err) => console.log(err) 
 
); 
 

 
// Inside DOMContentLoaded callback check if myData is available. 
 
document.addEventListener("DOMContentLoaded", (event) => { 
 
    let loop =() => { 
 
    \t (myData) ? console.log(myData) : window.setTimeout(loop, 100); 
 
    }; 
 
    loop(); 
 
});

+0

矢印機能の構文を理解するにはしばらく時間がかかりました。簡潔さのために、私は矢印の機能とは無関係の答えでこれらを使用しません。 もちろん、アプローチ自体は機能します。ありがとうございました。 – kleinfreund

+0

申し訳ありませんが、矢印機能のオーバーロードは、ちょうど最新の機能に慣れるようにしようとしています。 ;) – DavidDomain

+0

私はちょうど '(data)=> myData = data'を2分間見ていて、本当に何があったのか分かりませんでした。しかたがない。 – kleinfreund

1

私はいくつかの非同期イベントを待つ必要がある場合には、私の個人的なアプローチは、通常、これらの線に沿って何かである:

const EVENT0 = 0x1; 
const EVENT1 = 0x2; 
const ALL_READY = 0x3; 

var ready = 0; 
init(); 

function init() { 
    asyncRequest0(); 
    asyncRequest1(); 

    var waitFunc; 

    (waitFunc = function() { 
    if(ready == ALL_READY) { 
     goOn(); 
    } 
    else { 
     setTimeout(waitFunc, 10); 
    } 
    })(); 
} 

function goOn() { 
    console.log('Here we go!'); 
} 

function asyncRequest0() { 
    // some callback will do: 
    ready |= EVENT0; 
} 

function asyncRequest1() { 
    // some callback will do: 
    ready |= EVENT1; 
} 

だから、基本的には、各非同期イベントは、「準備」変数にビットをセットします仕事が完了したらwaitFunc()関数は、すべてのイベントが完了するのを待っています(順序は関係なく)。そしてgoOn()関数を呼び出します。

は今、あなたのコードに適用される:

const DOM_READY = 0x1; 
const XHR_READY = 0x2; 
const ALL_READY = 0x3; 

var ready = 0; 
var listData; 

init(); 

function init() { 
    document.addEventListener("DOMContentLoaded", function() { 
    ready |= DOM_READY; 
    }); 

    var xhr = new XMLHttpRequest(); 

    xhr.open('GET', 'records.json', false); 
    xhr.addEventListener("load", function() { 
    if (xhr.readyState === 4) { 
     if (xhr.status === 200) { 
     listData = JSON.parse(xhr.responseText); 
     } else { 
     listData = false; 
     console.error('Error: ' + xhr.status + ' ' + xhr.statusText); 
     } 
     ready |= XHR_READY; 
    } 
    }); 
    xhr.addEventListener("error", function() { 
    listData = false; 
    ready |= XHR_READY; 
    console.error('Error: ' + xhr.status + ' ' + xhr.statusText); 
    }); 
    xhr.send(null); 

    var waitFunc; 

    (waitFunc = function() { 
    if (ready == ALL_READY) { 
     goOn(); 
    } else { 
     setTimeout(waitFunc, 10); 
    } 
    })(); 
} 

function goOn() { 
    console.log('Here we go!'); 
    // do something with listData 
} 

あなたは本当にわずか2イベントのために、ここでビットマスクを必要としませんが、それは一度に複数のイベントをテストするための便利な方法です。

+0

この方法が有効です。なぜ私がunderatandしていないのは、 'listData'を利用できないときに' false'にセットする理由です。なぜそれを未定義にしないのですか? – kleinfreund

+0

@kleinfreundあなたは間違いなく同様にそれを残すことができます。 (私は通常、「間違った」結果を「偽」に設定しますが、特別な理由はありません) – Arnauld

関連する問題