Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
42
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

Node.jsでコールバック関数を書く

最もシンプルな例

StackOverflowから引用
http://stackoverflow.com/questions/2190850/create-a-custom-callback-in-javascript

simple.js
function doSomething(callback) {
    // Call the callback
    callback('stuff', 'goes', 'here');
}
function foo(a, b, c) {
    // I'm the callback
    console.log(a + " " + b + " " + c);
}
doSomething(foo);

実行するとdoSomething >> fooの順番に処理が行われます。
しかし、これを多段にすると

multi_callbacks.js
var log = '';
function step1(callback) {
    log += 'step1 ';
    callback();
}
function step2(callback) {
    log += 'step2 ';
    callback();
}
function step3(callback) {
    log += 'step3 ';
    callback();
}
function step4(callback) {
    log += 'step4 ';
    callback();
}
step1(function(){
    step2(function(){
        step3(function(){
            step4(function(){
                console.log('log:', log); //log: step1 step2 step3 step4
                console.log('done');
            })
        })
    })
});

あっという間にコールバック地獄となります。

コールバックのnestを回避する

コードがネストし過ぎて辛いので、コールバックの仕組みを作ります。

emitter.js
// Perfect JavaScriptより
var str = '';
var emitter = {
    callbacks: [], //コールバック関数の置き場
    register: function(fn) { // コールバック関数を登録する
        this.callbacks.push(fn);
    },
    onOpen: function() { //発火装置
        this.callbacks.forEach(function(f) {
            f();
        });
    }
};

// emitterを使ってみる
emitter.register(function() {
    str += "Sunday ";
});
emitter.register(function() {
    str += "Monday ";
});
emitter.register(function() {
    str += "Tuesday ";
});
emitter.onOpen();
// 結果確認
console.log(str); //Sunday Monday Tuesday

コールバック関数に、クロージャを利用する

クロージャを利用することで、コールバック関数に状態を持たせることができます。
>> クロージャの記事も書きました!

callback_closure.js
// クロージャを登録する
var emitter = {
    callbacks: [],
    register: function(fn) {
        this.callbacks.push(fn);
    },
    onOpen: function() {
        this.callbacks.forEach(function(f) {
            f();
        });
    }
};

// 即時関数を使ったクロージャを登録する
emitter.register(function() {
    var name = 'dog';
    return (function() {
        console.log(name+' is called!');
    }());
});
emitter.register(function() {
    var name = 'cat';
    return (function() {
        console.log(name+' is called!');
    })();
});
emitter.onOpen();

実際はemitterのような仕組みを自力で作るのは大変なので、npmパッケージを利用したりします。
その一例ですが >> Promise-Qの使い方

2つのファイル間でコールバックする

まずはお題のサンプルを作ります。

family_simple.js
var my_family = {
    members:[],
    add_member: function(m){
        this.members.push(m);
    },
    show_members: function() {
        var members = this.members;
        setTimeout(function(){
            members.forEach(function(m){
                console.log(m);
            });
        }, 500);
    }
};
my_family.add_member('hoge'); //hogeをpush
my_family.add_member('fuga'); //fugaをpush
my_family.show_members(); //membersを順に表示する

この処理を2つのファイルに分けて、コールバックさせるよう書いてみます。

family.js
module.exports = {
    members : [],
    add_member: function(name) {
        this.members.push(name);
    },
    get_members: function(callback) {
        var members = this.members;
        setTimeout(function(){
            callback(members);
        }, 500);
    }
};
family_manager.js
var my_family = require('./family');
my_family.add_member('hoge');
my_family.add_member('fuga');

var callback = function(members){
    members.forEach(function(name){
        console.log(name);
    });
};
my_family.get_members(callback);

このようにrequireとmodule.exportsを使って、異なるファイル間でcallbackのやり取りができます。
ただ、この例だとfamily.jsのmembersに直接参照できてしまうので、それを避けたい場合は次のような書き方もできます。

family2.js
module.exports = (function(){
    var members = [];
    function add_member(name) {
        members.push(name);
    }
    function get_members(callback) {
        setTimeout(function(){
            callback(members);
        }, 500);
    }
    return {
        add_member: add_member,
        get_members: get_members
    };
}());
family_manager2.js
var my_family = require('./family2');
my_family.add_member('hoge');
my_family.add_member('fuga');

var callback = function(members){
    members.forEach(function(name){
        console.log(name);
    });
};
my_family.get_members(callback);
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
42
Help us understand the problem. What are the problem?