質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
XHR

XHR(別名XMLHttpRequest)はJavaScriptなどのスクリプト言語を使ってサーバーとHTTP通信を行うAPIを指します。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

Q&A

解決済

2回答

936閲覧

xhr.onreadystatechange内でカウントしたものが外で未定義になってしまう

poppoas

総合スコア12

XHR

XHR(別名XMLHttpRequest)はJavaScriptなどのスクリプト言語を使ってサーバーとHTTP通信を行うAPIを指します。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

0グッド

0クリップ

投稿2020/03/06 02:46

前提・実現したいこと

AjaxでフィードURLから取得したアイテムの件数を外でも使用したい

発生している問題・エラーメッセージ

xhr.onreadystatechange(function(){})内で取得したアイテムの件数を外の変数に加算しているのですが、
コンソール上で確認すると「undefind」未定義であると表示されます。

以下は該当部分のサンプルです。

該当のソースコード

Typescript

1private _feedItemCount=0; 2 3info(){ 4 var domParser = DOMParser(); 5 var xhr = new XMLHttpRequest(); 6 xhr.open('GET',feedURL); 7 xhr.send(); 8 9 xhr.onreadystatechange = function() { 10 11 if(xhr.readyState === 4 && xhr.status === 200){ 12 13 if(typeof(xhr.response) === 'string'){ 14 15 try{ 16 var feed_data:Document = domParser.parseFromString(xhr.response, "text/html"); 17 var items = feed_data.getElementsByTagName('item'); 18 19 //総取得件数に加える 20 _feedItemCount+= items.length; 21 }catch{} 22 } 23 } 24 } 25 26 console.log(_feedItemCount); //undefind 27}

試したこと

xhr.onreadystatechangeの中でitems.lengthを確認したところ、問題なく取得件数をとれており、_feedItemCountに加算して
コンソールで確認しても、問題はありませんでした。

何か見落としていることがあるのでしょうか?

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kairi003

2020/03/06 06:00

privateで宣言していますがクラス内なのでしょうか?
poppoas

2020/03/06 06:27

クラス内だからprivateで宣言しています。
guest

回答2

0

ベストアンサー

AjaxでフィードURLから取得したアイテムの件数を外でも使用したい

Ajax は非同期処理なので、順番の問題です。
ご質問の console.log()xhr.readyState が 4未満の時点で実行されています。
このため、値は未代入で、初期値(undefined)が出力されています。

対策は、次の2つです。

  1. async/await でフロー制御できる fetchを使う(PromiseベースのXHRと考える)。
  2. 古典的な手法として、callback で変数への代入を検知する(あるいは、値を受け取る)。

コメントを受けて

fetch はモダン環境のJavaScriptにおいては標準です。
MDN fetch ブラウザサポート状況

ご質問のタグには「TypeScript」があり、「IEサポートの継続 が要件にある」場合は、コールバックによる手法になりそうです。

javascript

1//private _feedItemCount=0 2var _feedItemCount=0; 3 4function getFeedItems(url, callback) { 5 var domParser = DOMParser(); 6 var xhr = new XMLHttpRequest(); 7 xhr.open('GET',url); 8 xhr.send(); 9 10 xhr.onreadystatechange = function() { 11 var feed_data, feed_items; 12 if(xhr.readyState === 4 && xhr.status === 200){ 13 14 if(typeof(xhr.response) === 'string'){ 15 try{ 16 //var feed_data:Document = domParser.parseFromString(xhr.response, "text/html"); 17 feed_data = domParser.parseFromString(xhr.response, "text/html"); 18 feed_items = feed_data.getElementsByTagName('item'); 19 } 20 //catch {} 21 catch(e) {} 22 23 callback( feed_items ); 24 } 25 } 26 } 27}; 28 29getFeedItems(feedURL, function( items ){ 30 _feedItemCount += items.length; 31 console.log( _feedItemCount ) 32})

※TypeScript はよくわかってないので、TypeScript独特の文と感じた箇所をコメントアウトし、JavaScript でサンプルを追記させていただきました。

もし、TypeScript でトランスパイル後、 undefined になるようでしたら、スコープも疑ってみてください。

投稿2020/03/06 05:53

編集2020/03/06 06:40
AkitoshiManabe

総合スコア5432

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

poppoas

2020/03/06 06:08

ありがとうございます。 ご指摘の通り、恐らく非同期処理での順番が問題ですね。 fetchは初めてききましたが、これはJavaScript標準のものですか? 今実装をしているXHRと置き換えて、XHRと同じように利用できるというものでしょうか? callbackでの変数への代入を検知というのは、どういう感じになりますでしょうか? ちょっとイメージがわかないので、もしお時間よろしければご教示いただけないでしょうか? 質問ばかりで申し訳ありませんが、よろしければお教えください。
poppoas

2020/03/06 06:30

すみません、fetchはIEでの利用ができないみたいなので、今回の仕様では利用できないものでした。 callbackのやり方だけでもご教示頂ければ幸いです。
AkitoshiManabe

2020/03/06 06:43 編集

追記しました。TypeScript の型付けが必要な箇所は修正してください。 > callbackでの変数への代入を検知 「callback が実行されたかどうかで判断する」というものです
poppoas

2020/03/06 06:50

ご丁寧にわかりやすい回答ありがとうございます。 callbackを利用したことがなかったので、まだピンときていないのですが、 教えていただいたコード上でいうなら callback(feed_items) が呼ばれたということはその下にある getFeedItems(feedURL, function( items ){ _feedItemCount += items.length; console.log( _feedItemCount ) }) が実行されて、カウンタが増加するといった感じの解釈でしょうか?
AkitoshiManabe

2020/03/06 06:53

そのとおりです。 xhr.readyState===4 は 通信完了ですので、 DOMParser で フィードを解析後、item 要素の一覧(コレクション)を得る方法を考えてみました。
poppoas

2020/03/06 06:55

なるほどですね。やっと腹落ちしました。 このやり方で今一度やってみたいと思います。 最後までご丁寧にありがとうございました。
poppoas

2020/03/06 07:11 編集

※すみません、自己解決しました。 度々すみません、良ければお教えいただきたいのですが、 callbackする関数は自分自身でないといけませんか? getFeedItemsCount( function( items ){ _feedItemCount += items.length; console.log( _feedItemCount ) }) のようにコールバックする関数を別の関数にしても同じ結果になるでしょうか?
AkitoshiManabe

2020/03/06 07:14

callbackは イベントリスナみたいな関数という考え方がわかりやすいと思います。実行時、引数に渡されるのが event オブジェクトではなく、 callback() の実行箇所で指定した「任意の引数」になる点が イベントリスナとの違いです。 callback の処理内容も必要に応じて変えることができますので、仮にインスタンスのメソッドで受け取りたい場合、oj.method.bind(oj) とすることもできます(TypeScriptでの記述方法はチョット分かりませんが)。
poppoas

2020/03/06 07:21

すみません、またご丁寧にありがとうございます。 すごく助かります。 >callbackは イベントリスナみたいな関数という考え方がわかりやすいと思います これすごくわかりやすいなと感じました。 callback()の実行箇所がイベントの発行場所になるイメージですね。 またその際にはcallback()で指定した「任意の引数」になるイメージ。とてもわかりやすいです。 ありがとうございます!!
guest

0

privateで宣言されていることから、これは変数はなくクラスのフィールド定義に見えるのですが、それならインスタンスが自己参照するためにthis._feedItemCountとする必要があります。
なので宣言さてれていない変数_feedItemCountundefinedです。
また他の回答者さんも言っていますがxhrは非同期なのでこのconsole.logだと0になります。

itemsのことなら「外の変数」には見えませんが…
グローバル変数ならonreadystatechangeの前くらいに宣言しないと意味ないです。

投稿2020/03/06 03:39

編集2020/03/06 06:08
kairi003

総合スコア1330

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

poppoas

2020/03/06 03:48

_feedItemCount+= items.length; itemsの要素の数はグローバル変数であるfeedItemCountに追加してます。
kairi003

2020/03/06 05:57

すみません、勘違いしてました
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問