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

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

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

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

Q&A

解決済

1回答

1706閲覧

Node.js において、JSONをパースして抽出した文字列が関数の外で使えない

nori3

総合スコア8

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

0グッド

1クリップ

投稿2019/02/27 11:48

前提・実現したいこと

SLACKのユーザIDから表示名(display_name)を得るスクリプトをサーバ上の Node.js で実現しようと思いました。
そこで Windows 版の node.js v10.15.1 を使って、サーバから取得した JSONをパースして、必要な値をget_user_name関数の外側で使いたいと考えました。

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

ログ(result_txtの値)が出力されない(正確には、改行が1行出力される)。

該当のソースコード

Node.js

1var get_user_name = require('request'); 2global.result_txt=""; 3 4const url1 = 'https://slack.com/api/users.info?token=xoxp-HIMITSU-HIMITSU-HIMITSU&user='; 5const url2 = '&pretty=1'; 6 7user = 'X1X1X1XX1'; 8 9var options = { 10url: url1+user+url2, 11method: 'GET', 12json: true 13} 14 15get_user_name(options,function (error, respinse,body ){ 16 result_txt=(body.user.profile.display_name).toString; 17}) 18 19console.log(result_txt);

試したこと

Node.js

1get_user_name(options,function (error, respinse,body ){ 2 result_txt=(body.user.profile.display_name).toString; 3    console.log(result_txt); 4})

とすると、正しく表示ユーザがログに取得できますが、関数外で使いたいので、

Node.js

1get_user_name(options,function (error, respinse,body ){ 2 result_txt=(body.user.profile.display_name).toString; 3}) 4console.log(result_txt);

と書いても動く形に持っていきたいです。単純な発想で、Global変数を使えば解決できると思ったのですが、どうやら、関数の中の変数と外の変数で、型が違うのでこの問題が起きているような気がします。

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

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

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

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

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

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

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

guest

回答1

0

ベストアンサー

Node.js名物の非同期処理の洗礼ですね。
「非同期処理」や「コールバック地獄」などのワードでググってお勉強してください。


ブラウザに搭載されているJavaScriptと共通の話ですが、
JSはシングルスレッドで動作します。
JSの実行中はブラウザの応答が完全に止る仕様であるためスリープみたいなことは実現不可能です。
とにかくさっさと実行して終了させる必要がある。

しかし、そこで壁になるのがI/O処理です。
ファイルの読み書き・ネットワークのリクエスト……といった事は処理にいくら時間がかかるのか読めない部分があります。
Ajaxの為にHTTPリクエスト投げた瞬間ブラウザが固まるとかユーザビリティ糞じゃん。

そこで、JSはイベント駆動という考え方をします。
ブラウザが用意している「イベント置き場」というのがありまして、
そこに「イベント完了条件」と「実行したい内容の関数」をセットで登録します。

ブラウザ内では無数のイベントが飛び交っています。
「マウスカーソルが動いた時」「HTTP通信が完了した時」「マウスで何かの要素をクリックした時」
ブラウザは絶えずイベント置き場を巡回しており、完了したイベントが見つかり次第対応する関数を実行という事を行っています。

質問文のコードで解説します。

JavaScript

1get_user_name(options,function (error, respinse,body ){ 2 result_txt=(body.user.profile.display_name).toString; 3}) 4console.log(result_txt);

get_user_name関数を発火させた時、
第二引数で「終わったら実行して欲しい関数」をコールバックとして渡しています。
get_user_nameの中身はrequestによりHTTPリクエストを発射しているはずなので、
待ちぼうけするには長いので、そのコールバック関数はそのまんまイベント完了時に実行される関数に登録します。
そのまま「イベント登録終わったから次いこう」と関数の処理が終わります。

イベントの巡回&コールバック関数を取り出して実行されるタイミングは、
JSの実行が全て完了して暇になってからです。

なので、例えローカルマシン内で次の行が実行される前に結果を受け取ったとしても、
質問文のコードでは先にconsole.log(result_txt);が実行されます。
終わって暇になり、イベントの巡回を待ってからコールバック関数が実行されてresult_txt変数の中身が書き換わります。

この辺が非同期処理の仕組みになっています。
解決法は単純、「コールバック関数の中身で後続のやりたい処理を全部書け」
何の解決にもなりませんが、こうすれば使えます。

JavaScript

1get_user_name(options,function (error, respinse,body ){ 2 result_txt=(body.user.profile.display_name).toString; 3 console.log(result_txt); 4})

「後続のやりたい処理を全部書け」って中々クレイジーですね。
非同期処理でやりたい事が増えれば増える程ネストが深くなり糞みたいなコードになります。

これを「コールバック地獄」と呼びます。


何か対策はないの?

ベストプラクティスを紹介します。

まずはPromiseを覚えて使いましょう。
ある程度使いこなせるようになったらasync/awaitという構文を覚えましょう。

async/awaitはPromiseを良い感じに扱う糖衣構文で、
ここまで学習が進めば非同期処理は全く怖くありません。

実は今回使っているrequestモジュールですが、これのPromise版があります。
調査して使ってみてください。

request-promiseを使ったHTTPクライアントを作る - Qiita

投稿2019/02/27 12:07

編集2019/02/28 11:11
miyabi-sun

総合スコア21158

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

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

nori3

2019/02/28 11:46

丁寧な回答まことにありがとうございました。Promiseは手元の本の項目にあったのですが、難しそうと思って読み飛ばしてしまいました。 私が非同期処理の根本を理解していなかったことが分かりましたので、教えていただいたサイトやキーワードを中心に勉強してみたいと思います。手続き型プログラミング言語しか触っていなかった学生時代SQLに出会ったときと同様のショックがありました。 とりあえず今回は実装を急ぐ必要があったのでコールバックに後続処理を追加し、想定の結果を得ることが出来ました。重ねて御礼申し上げます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問