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

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

ただいまの
回答率

88.10%

node.js(passport)のfunction()やdone()の意味、動きを教えてください

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 5,417

score 7

前提・実現したいこと

node.js + express + passportでGoogleアカウントでログインする機能を作っています。
passportのGuideを見ても意味がわからない状態です。

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

function()とdone()がどうのようなことを行っているのかわかりません。
例として、以下のようにpassportのコードがあった場合、
fucntion()とdone()が何をしているのか教えてください

passport.serializeUser(function(user, done) {
  done(null, user.id);
});

passport.deserializeUser(function(id, done) {
  User.findById(id, function(err, user) {
    done(err, user);
  });
});

試したこと

ぐぐったところ、function()はコールバック関数という名前らしいですが、
function()の引数の説明や、function(res,req)やfunction(user,done)が実際にどのような意味を持っているのかまったくわかりませんでした。
done()についても同様です。

上記コードをもとに説明をお願いします。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+4

コールバック関数等に関しては,下記記事に詳細な解説があるので読んでみてください.

JavaScript中級者への道【5. コールバック関数】 - Qiita
(発展) JavaScriptは如何にしてAsync/Awaitを獲得したのか Qiita版 - Qiita
(補足)日本語のコードで理解するPromise - Qiita

ここでのfunction () { ... } は「準備ができたタイミングで関数を呼ぶ」の対象となるそれです.


Passportのdoneに関しては以下に日本語で説明があります.

Passport | 設定

要するに,doneはEventEmitter的に,「成功したよ」「失敗したよ」とPassport内部に対して通知するイベントを発行しているようなものでしょうか.

このようにストラテジーに処理を委譲することで、検証用コールバックは、Passportとデータベースを疎に保ちます。 これによりアプリケーションは、認証レイヤーでの様々な前提条件にとらわれず、ユーザー情報をどのように格納するか自由に選択できます。

  • serializeUser 「ユーザオブジェクトを与えてやるから,保存に成功したらdoneの第2引数ででユーザIDを渡してくれ.エラーがあったときは第1引数にそれを渡してくれ.続く処理は俺(Passport)がやるからとりあえずその仕事だけよろしく.」
  • deserializeUser 「ユーザIDを渡してやるから,復元に成功したらdoneの第2引数でユーザオブジェクトを渡してくれ.エラーがあったときは第1引数にそれを渡してくれ.続く処理は俺(Passport)がやるからとりあえずその仕事だけよろしく.」

こういうことですね.実際にどのように保存や復元を行うかはプログラマが自由に選択できるということです.関数の返り値を使わずにわざわざdoneを実行させるのは,JavaScriptはおもに,非同期処理の実現にはコールバック関数モデルを採用しているからです.

User.findById(id, function(err, user) {
  /* 成功または失敗時の処理 */
});

User.findByIdがこのような使い方である以上,完了時にdoneを呼ばせるしかないですよね.
(もしPromiseを返すような設計になっていれば必ずしもそうではないのですが…)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/10/06 08:06

    また,Expressはコールバック関数ベースの設計となっていて扱いにくいので,それをPromiseベースにして使いやすくしてくれた koa というフレームワークも存在します.個人的にはこちらのほうが断然おすすめです.

    キャンセル

  • 2016/10/06 12:12

    JavaScriptの非同期処理を完全に理解するには,

    コールバック関数 → Promise → Generator → async/await

    という4段階の歴史的な発展を理解する必要がありますが,少しずつ学んでいってください.最初の鬼門はPromiseかもしれません.

    キャンセル

+2

良い質問だと思います。まず、Javascript の非同期プログラミングの構造を覚えて頂く必要があります。少し長くなります。

言語を問わず、プログラムがネットワークで通信する、ファイルを読む、データベースを読むなど外の世界へアクセスするときは、処理に時間がかかる場合があり、実行を開始すると終了するまで待つ必要があります。javascript 以外の言語では、外部へアクセスするライブラリを呼び出すと、結果が返されます。これは外部へのアクセスを開始してから終了するまで一時的にプログラムを停止して待ち、結果を受け取って返すことで実現されています。

javascript はシングルスレッドで動く仕様となっており、プログラムの実行を止めて待つと、すべての処理が止まってしまい性能が出ません。そこで、アクセスを開始する命令を出した後、終わったときに関数を呼び出してもらうという方法で続きの処理を実行します。たとえば、ファイルから読み出すときは、

var fs = require('fs');
function test() {
  fs.readFile('./test.txt', 'utf8', function (err, text) {
    console.log('end.');
    console.log(text);
  });
  console.log('start.');
}
test();

のように書きますが、test関数から fs.readFile 関数を呼び出して、その呼出が終わった時点では、コールバックは呼び出されていません。いつ呼び出されるかというと、コンソールに'start.'を印刷して test() の実行が終わった後です。他にも外部へのアクセスをした結果を待っているものがあれば、早いもの順でコールバックが呼び出されます。このコールバック関数は Javascript の処理系から呼び出されるので、プログラマの書いたコードから呼び出されるものではありません。

さて、上記の例でファイルを読み出した後の処理はすべてコールバック関数の中で実行しなければならないことに注意してください。すると、test()関数の中でプログラムの続きの処理を全部記述しなければならなくなり、サブルーチン化、モジュール化がまったくできなくなってしまいます。そこで、サブルーチンにコールバックを渡して、続きの処理を呼び出し側で記述できるようにします。例えば、

var fs = require('fs');
function test(done) {
  fs.readFile('./test.txt', 'utf8', function (err, text) {
    console.log('end.');
    done(text)
  });
  console.log('start.');
}

test(function (text) {
     console.log(text);
});

のように修正することで、 test() 関数はファイルを読み出し、その続きの処理(ここでは、コンソールにファイルの内容を印刷する)は呼び出し側に記述できるようになりました。

Javascript では、他のモジュールの関数を呼び出すと、いつ非同期が呼び出されるのかわからないので、常にこの作法に従う必要があります。

passport もこの作法に従っているわけです。function(user, done) {done(null, user.id);}); は他の言語なら function(user) {return user.id;}); となるところですが、上記のマナーにしたがって、返却値ではなく、コールバック関数の引数で値を返しているわけです。

例にあげてもらったコードですと、コールバックの中に非同期処理が含まれておらず、コールバック関数が呼ばれるとすぐに done を呼ぶことになるので、なぜ返却値で返さないのかわからないと思います。シリアライズやデシリアライズの処理中に外部へのアクセス(例えばデータベースへのアクセス)が途中で必要になる場合にどう記述するか考えていただければ、なぜ、このような構造になっていることがわかるかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

JavaScriptの関数は数値や文字列と同じような扱いが出来る言語です。
C言語などでは考えも付かないこんな使い方ができます。

// 変数に代入する
var add_2 = function (it) { return it + 2; };
console.log(add_2(3)); # 5

// 関数を戻り値として返す
var add = function (a) {
  return function (b) { return a + b; };
};
console.log(add(3)(4)); # 7

// 関数を引数に設定
var hoge = function (cb) {
  return cb(4, 5);
}
console.log(hoge(function(a, b){ return a + b; })); # 9

以上、関数がまるで数値や文字列のように扱えることがわかりました。
関数がこの特性を持つ事を第一級関数と呼びます。

Node.jsでは非同期処理を実現するために
関数を引数に設定できるという特性をフル活用します。

Webサーバーやデータベースサーバーにリクエストを投げた後なんて、レスポンスが帰ってくるまでCPUはすること無いから暇なんです。
Node.jsでは「終わったら引数として渡しておいた関数実行しといてね、あとよろしく。」…という使い方をします。

後の説明はmpywさん、mit0223さんが詳しくされていますので割愛します。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 88.10%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る