Middle-end w oparciu o NodeJS

by DevMeetings (Piotrek Koszuliński; @reinmarpl; http://code42.pl)

Wielki nieobecny

D

Wielki nieobecny

D

Organizatorzy

O

CSJS vs SSJS

Client-side JavaScript

Server-side JavaScript (w NodeJS)

Asynchroniczna pułapka

Złap errora – def czy call?

try {
    var fn = function () {
        throw new Error();
    };
}
catch (e) {
    console.log('def', e);
}
 
try {
    setTimeout(fn, 1);
}
catch (e) {
    console.log('call', e);
}

Asynchroniczna pułapka

Złap errora – def czy call?

Facepalm by David

Asynchroniczna pułapka

Złap errora – nie tak, to jak?

process.on('uncaughtException', function (err) {
    console.log('ups', err);
});

Asynchroniczna pułapka

Typowy przypadek

app.get('/post/:title', function (req, res) {
    db.getPostByTitle(req.params.title, function (err, post) {
        if (err) throw err;
        db.getComments(post.id, function (err, comments) {
            if (err) throw err;
            res.render({
                post: post,
                comments: comments
            });
        });
    });
});

Asynchroniczna pułapka cz.2.

Konkatenacja plików w katalogu

  1. odczytanie listy plików w katalogu
  2. odczytanie zawartości każdego z plików
  3. synchronizacja!
  4. połączenie zawartości
  5. zapisanie pliku wynikowego

Asynchroniczna pułapka cz.2.

Konkatenacja plików w katalogu

fs.readdir(__dirname, function (err, names) {
    var l = names.length, opened = 0, content = [];
    names.forEach(function (name, i) {
        fs.readFile(name, 'utf-8', function (err, c) {
            content[i] = c;
            if (++opened === l) write();
        });
    });
    var write = function () {
        fs.writeFile('lib.js', content.join("\n"), function (err) {
            console.log('done');
        });
    };
});

A można łatwiej?

A zgadnij

Więcej:

Async

Async

Typowy przypadek

app.get('/post/:title', function (req, res) {
    async.waterfall([
        function (callback) {
            db.getPostByTitle(req.params.title, callback);
        },
        function (post, callback) {
            db.getComments(post.id, function (comments) {
                callback(null, post, comments);
            });
        }
    ],
    function (err, post, comments) {
        if (err) throw err;
        res.render({
            post: post,
            comments: comments
        });
    }
});

Async

Typowy przypadek

Deferred i promise'y

Załóżmy, że mamy asynchroniczną funkcję:

var oneOneSecondLater = function (callback) {
    setTimeout(function () {
        callback(1);
    }, 1000);
};
 
oneOneSecondLater(function (v) { console.log(v); });

Deferred i promise'y

Spróbujmy inaczej:

var maybeOneOneSecondLater = function () {
    var callback;
    setTimeout(function () {
        callback(1);
    }, 1000);
    return {
        then: function (_callback) {
            callback = _callback;
        }
    };
};
 
maybeOneOneSecondLater().then(function (v) {
    console.log(v);
});

Deferred i promise'y

Po drobnej reaktoryzacji:

var defer = function () {
    var callback = null, _value;
    return {
        resolve: function (_value) {
            value = _value;
            callback(value);
            callback = undefined;
        },
        then: function (_callback) {
            if (callback === null)
                callback = _callback;
            else
                _callback(value);
        }
    }
};
 
var oneOneSecondLater = function () {
    var result = defer();
    setTimeout(function () {
        result.resolve(1);
    }, 1000);
    return result;
};
 
oneOneSecondLater().then(callback);

Deferred i promise'y

Deferred by Medikoo

Deferred i promise'y

Deferred by Medikoo

var later = function () {
  var d = deferred();
    setTimeout(function () {
        d.resolve(1);
    }, 1000);
    return d.promise;
};
 
later().then(function (n) {
    console.log(n); // 1
});

Ale promise, to tak naprawdę then, więc prościej:

later()
(function (n) {
    console.log(n); // 1
});

Deferred i promise'y

Deferred by Medikoo

Deferred i promise'y

Deferred by Medikoo

Deferred i promise'y

Deferred by Medikoo

Konkatenacja plików w katalogu:

all(
    // Read all filenames in given path
    a2p(fs.readdir, __dirname),
 
    // Read files content
    function (files) {
        return join(files.map(function (name) {
            return a2p(fs.readFile, name, 'utf-8');
        }));
    },
 
    // Concat into one string
    function (data) {
        return data.join("\n");
    },
 
    // Write to lib.js
    ba2p(fs.writeFile, __dirname + '/lib.js')
).end();

Deferred i promise'y

Deferred by Medikoo

W skrócie:

all(
    a2p(fs.readdir, __dirname),
 
    invoke('map', function (name) {
        return a2p(fs.readFile, name, 'utf-8');
    }), join,
 
    invoke('join', "\n"),
 
    ba2p(fs.writeFile, __dirname + '/lib.js')
).end();

Deferred i promise'y

Deferred by Medikoo

Nasz typowy przypadek:

all(
    a2p(db.getPostByTitle.bind(db), 't'),
    function (post) {
        return a2p(db.getComments.bind(db), post.id);
    }
)
(function (args) {
    res.render({
        post: args[0],
        comments: args[1]
    });
})
.end(function (err) {
    //sth
});

Deferred i promise'y

Deferred by Medikoo

Bądź gdybyśmy korzystali z deferred również w bazie:

all(
    db.getPostByTitle('t'),
    function (post) {
        return db.getComments(post.id);
    }
)
(function (args) {
    res.render({
        post: args[0],
        comments: args[1]
    });
})
.end(function (err) {
    //sth
});

Async czy deferred?

Asynchroniczna pułapka cz.3.

Jak debugować wywołania funkcji anonimowych?

Moduły w NodeJS

Moduły w NodeJS

Przykład

math.js:

exports.add = function () {
    var sum = 0, i = 0, args = arguments, l = args.length;
    while (i < l) {
        sum += args[i++];
    }
    return sum;
};

add.js:

var add = require('./math').add;
exports.increment = function (val) {
    return add(val, 1);
};

program.js:

var inc = require('./increment').increment;
var a = 1;
inc(a); // 2

Moduły w NodeJS

Moduły w NodeJS

Jak działają?

var exports = {}, module = { exports: exports };
function (exports, module, require) {
    // ciało modułu
}.call(exports, module, require);
 
exports; // publiczne API modułu

Przenośne moduły

modules-webmake

Przenośne moduły

RequireJS

Przenośne moduły

Podsumowanie

CSJS @ SSJS

CSJS @ SSJS

var jsdom = require('jsdom'),
    doc = jsdom.jsdom('<html><body></body></html>'),
    window = doc.createWindow();
window.console = console;
window.run(require('fs').readFileSync('jquery.js', 'utf-8'));
window.run('$("body").append("<p>Kopytko!</p>");');
window.run('console.log(document.innerHTML);');
// -> <html><body><p>Kopytko!</p></body></html>

Alternatywnie:

jsdom.env({
    html: '<html><body></body></html>',
    src: [
        require('fs').readFileSync('jquery.js', 'utf-8'),
        '$("body").append("<p>Kopytko!</p>");'
    ],
    done: function (err, window) { console.log(window.document.innerHTML); }
});
// -> <html><body><p>Kopytko!</p></body></html>
// -> <html><body><p>Kopytko!</p></body></html> (WTF?)

Middle-end

Middle-end

What sits between the front-end of a web application and the back-end of an application? The “middle-end”, naturally!

Kyle Simpson @getify
http://blog.getify.com/2010/07/how-to-begin-your-middle-end/

Middle-end

Zostawmy Kyle'a w spokoju

Zadania

System template'owy

Zadania

Routing

Zadania

Proxy v1

Zadania

Proxy v2

Zadania

Proxy v3

Zadania

Inne (może ciekawe)

Do roboty!

fight!
#

/