(Piotrek Koszuliński; @reinmarpl; http://code42.pl) @ MeetJS
require, fs.writeFileSync, fs.readFileSync)fs.readFile('sth.json', function (err, data) {
if (err) return console.log('ups!');
console.log(data);
});
def czy call?try {
var fn = function () {
throw new Error();
};
}
catch (err) {
console.log('def', err);
}
try {
setTimeout(fn, 1);
}
catch (err) {
console.log('call', err);
}
dziurawy catch...
...wywala nam serwer...
process.on('uncaughtException', function (err) {
console.log('ups', err);
});
Node'owa konwencja: fs.writeFile('sth', function (err, result) {});
Synchroniczne API:
app.get('/post/:title', function (req, res) {
var post = db.getPostByTitle(req.params.title);
var comments = post.getComments();
var tags = post.getTags();
res.render({
post: post,
comments: comments,
tags: tags
});
});
Asynchroniczne API:
app.get('/post/:title', function (req, res) {
db.getPostByTitle(req.params.title, function (err, post) {
post.getComments(function (err, comments) {
post.getTags(function (err, tags) {
res.render({
post: post,
comments: comments,
tags: tags
});
})
});
});
});
Synchroniczne API:
app.get('/post/:title', function (req, res) {
try {
var post = db.getPostByTitle(req.params.title);
var comments = post.getComments();
var tags = post.getTags();
res.render({
post: post,
comments: comments,
tags: tags
});
}
catch (err) {
res.render500(err);
}
});
Asynchroniczne API:
app.get('/post/:title', function (req, res) {
db.getPostByTitle(req.params.title, function (err, post) {
if (err) return res.render500(err);
post.getComments(function (err, comments) {
if (err) return res.render500(err);
post.getTags(function (err, tags) {
if (err) return res.render500(err);
res.render({
post: post,
comments: comments,
tags: tags
});
})
});
});
});
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');
});
};
});
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'); }); }; });
Ktoś kto...
series(tasks, [callback]) – Run an array of functions in series, each one running once the previous function has completed.parallel(tasks, [callback]) – Run an array of functions in parallel, without waiting until the previous function has completed.waterfall(tasks, [callback]) – Runs an array of functions in series, each passing their results to the next in the array.async.parallel([
function(callback){
setTimeout(function () {
callback(null, 'one');
}, 200);
},
function(callback){
setTimeout(function () {
callback(null, 'two');
}, 100);
},
],
function (err, results) {
results; // -> ['two', 'one']
});
app.get('/post/:title', function (req, res) {
async.waterfall([
db.getPostByTitle.bind(db, req.params.title),
function (post, callback) {
async.parallel({
c: post.getComments.bind(post),
t: post.getTags.bind(post)
},
function (err, results) {
callback(err, post, results.c, results.t);
});
}
],
function (err, post, comments, tags) {
if (err) return res.render500(err);
res.render({ ... });
});
});
Załóżmy, że mamy asynchroniczną funkcję:
var oneOneSecondLater = function (callback) {
setTimeout(function () {
callback(1);
}, 1000);
};
oneOneSecondLater(function (v) { console.log(v); });
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);
});
całość rozmyślań u Krisa Kowala (https://github.com/kriskowal/q/blob/master/design/README.js)
a2p(fs.readFile, __filename, 'utf-8')
(invoke('toUpperCase'))
(console.log)
.end();
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
});later()
(function (n) {
return n + 1;
})
(function (n) {
console.log(n); // 2
});
all(...):
all(p1, p2, p3)
(function (results) {
// results is array of resolved values of p1, p2 and p3.
});
join(...):
join(p1, p2, p3)
(function (results) {
// results is array of resolved values of p1, p2 and p3.
});
var a2p = deferred.asyncToPromise.call,
ba2p = deferred.asyncToPromise.bind;
a2p(fs.readFile, __filename, 'utf-8')
(function (content) {
// change content
return content;
})
(ba2p(fs.writeFile, __filename + '.changed'));
later()(function (n) { throw new Error('error!'); })
(function () {
// never called
}, function (err) {
// handle error
});
later()(function (n) { throw new Error('error!'); })
.end(function (err) {
// handle error
});
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();
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();
app.get('/post/:title', function (req, res) {
a2p(db.getPostByTitle.bind(db), req.params.title)
(function (post) {
return all(
post,
a2p(post.getComments.bind(post)),
a2p(post.getTags.bind(post))
);
})
(function (args) {
res.render({
post: args[0],
comments: args[1],
tags: args[2]
});
})
.end(function (err) {
res.render500(err);
});
});
Bądź gdybyśmy korzystali z deferred również w bazie:
app.get('/post/:title', function (req, res) {
db.getPostByTitle(req.params.title)
(function (post) {
return all(
post, post.getComments(), post.getTags()
);
})
(function (args) {
res.render({
post: args[0],
comments: args[1],
tags: args[2]
});
})
.end(function (err) {
res.render500(err);
});
});
Porównanie z synchronicznym API:
app.get('/post/:title', function (req, res) {
try {
var post = db.getPostByTitle(req.params.title);
var comments = post.getComments();
var tags = post.getTags();
res.render({
post: post,
comments: comments,
tags: tags
});
}
catch (err) {
res.render500(err);
}
});
> (function () { throw new Error('Ratunku'); }());
Error: Ratunku
at repl:1:22
at repl:1:44
at REPLServer.eval (repl.js:80:28)
> (function fn() { throw new Error('Ratunku'); }());
Error: Ratunku
at fn (repl:1:24)
at repl:1:46
at REPLServer.eval (repl.js:80:28)
/