Бабулины сказки
Тут все такие ахуенные, могут настроить по мануалу webpack и запрограммировать ангуляр и даже послать json по аджакс,
но как взглянешь на код - в рот мне ноги, такой пиздец!
поэтому для самых маленьких пишу этот высер
итак вы открыли ноду и охуели от того, что почти все функции "из коробки" последним аргументом принимают колбэк
var fs = require("fs");
fs.readdir(__dirname, function (error, files) {
if (error) {
console.error(error);
} else {
for (var i = 0, j = files.length; i < j; i++) {
console.log(files[i]);
}
}
});
это йобаный стыд, пиздец и прошлый век, никто так больше не кодит кроме хардкорных сишников
Пирамида смерти
а в чем собственно проблема?
проблема в том что на моём маке с ретиной заканчивается место под пробелы и весь код маячит далеко справа
при использовании хотя бы десятка таких функций подряд
var fs = require("fs");
var path = require("path");
var buffers = [];
fs.readdir(__dirname, function (error1, files) {
if (error1) {
console.error(error1);
} else {
for (var i = 0, j = files.length; i < j; i++) {
var file = path.join(__dirname, files[i]);
fs.stat(file, function (error2, stats) {
if (error2) {
console.error(error2);
} else if (stats.isFile()) {
fs.readFile(file, function (error3, buffer) {
if (error3) {
console.error(error3);
} else {
buffers.push(buffer);
}
});
}
});
}
}
});
console.log(buffers);
так что же этим можно сделать?
не применяя библиотек, для наглядности, так как с ними все мои примеры не займут и строчки кода
я покажу как с этой херней справиться используя сахар es6 и es7
Promise
охуенный встроенный объект позволяющий немного разровнять пирамиду
var fs = require("fs");
var path = require("path");
function promisify(func, args) {
return new Promise(function (resolve, reject) {
func.apply(null, [].concat(args, function (error, result) {
if (error) {
reject(error);
} else {
resolve(result);
}
}));
});
}
promisify(fs.readdir, [__dirname])
.then(function (items) {
return Promise.all(items.map(function (item) {
var file = path.join(__dirname, item);
return promisify(fs.stat, [file])
.then(function (stat) {
if (stat.isFile()) {
return promisify(fs.readFile, [file]);
} else {
throw new Error("Not a file!");
}
})
.catch(function (error) {
console.error(error);
});
}));
})
.then(function (buffers) {
buffers = buffers.filter(function(buffer){
return buffer;
});
console.log(buffers);
})
.catch(function (error) {
console.error(error);
});
кода стало немного больше, но зато сильно сократилась обработка ошибок
хочу сразу обратить внимание я использую .catch два раза потому, что Promise.all
использует fail-fast стратегию и бросает ошибку если ее бросил хотя бы один промис
на практике такое пременение далеко не всегда оправдано, например если нужно
проверить список проксей, то нужно проверить все, а не обламываться на первой дохлой
этот вопрос решают библиотеки Q и Bluebird и тд, поэтому я его не освещаю
теперь перепишем это все с учётом arrow functions, desctructive assignment и modules
import fs from "fs";
import path from "path";
function promisify(func, args) {
return new Promise((resolve, reject) => {
func.apply(null, [...args, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
}]);
});
}
promisify(fs.readdir, [__dirname])
.then(items => Promise.all(items.map(item => {
let file = path.join(__dirname, item);
return promisify(fs.stat, [file])
.then(stat => {
if (stat.isFile()) {
return promisify(fs.readFile, [file]);
} else {
throw new Error("Not a file!");
}
})
.catch(console.error);
})))
.then(buffers => {
buffers = buffers.filter(e => e);
console.log(buffers);
})
.catch(console.log);
вот теперь совсем хорошо, но...
Generator
Но есть еще какие-то генераторы, которые добавляют новый тип функций function* и ключевое слово yeild
что будет если использовать их?
import fs from "fs";
import path from "path";
function promisify(func, args) {
return new Promise((resolve, reject) => {
func.apply(null, [...args, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
}]);
});
}
function * main() {
let items = yield getItems();
let files = yield checkItems(items);
let buffers = yield readFiles(files);
return buffers;
}
function getItems() {
return promisify(fs.readdir, [__dirname]);
}
function checkItems(items) {
return Promise.all(items.map(file => promisify(fs.stat, [path.join(__dirname, file)])
.then(stat => {
if (stat.isFile()) {
return file;
} else {
throw new Error("Not a file!");
}
})
.catch(console.error)))
.then(files => {
return files.filter(file => file);
});
}
function readFiles(files) {
return Promise.all(files.map(file => {
return promisify(fs.readFile, [file]);
}));
}
let generator = main();
generator.next().value.then(items => {
return generator.next(items).value.then(files => {
return generator.next(files).value.then(buffers => {
console.log(buffers);
})
})
});
честно, как по мне, так херня вышла, особенно в последнем куске кода
цепочки из generator.next().value.then хочеться убить не меньше чем колбэки из первого примера
однако это не значит, что генераторы плохие, они просто слабо подходят под эту задачу
Async/Await
еще два ключевых слова, с мутным значением, которые я попробую прилепить к решению уже надоевшей задачи по чтению файлов
import fs from "fs";
import path from "path";
function promisify(func, args) {
return new Promise((resolve, reject) => {
func.apply(null, [...args, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
}]);
});
}
async function main() {
let items = await getItems();
let files = await checkItems(items);
let buffers = await readFiles(files);
return buffers;
}
function getItems() {
return promisify(fs.readdir, [__dirname]);
}
function checkItems(items) {
return Promise.all(items.map(file => promisify(fs.stat, [path.join(__dirname, file)])
.then(stat => {
if (stat.isFile()) {
return file;
} else {
throw new Error("Not a file!");
}
})
.catch(console.error)))
.then(files => {
return files.filter(file => file);
});
}
function readFiles(files) {
return Promise.all(files.map(file => {
return promisify(fs.readFile, [file]);
}));
}
main()
.then(console.log)
.catch(console.error);
Пожалуй самый красивый пример, все функции заняты своим делом и нету ёбаных пирамид
А если бы я писал этот код не для учебника, то получилось бы как-то так
import bluebird from "bluebird";
import fs from "fs";
import path from "path";
let myfs = bluebird.promisifyAll(fs);
async function main(dirname) {
let items = await getItems(dirname);
let files = await checkItems(items);
let buffers = await readFiles(files);
return buffers;
}
function getItems(dirname) {
return myfs.readdirAsync(dirname)
.then(items => {
return items.map(item => path.join(dirname, item))
});
}
function checkItems(items) {
return bluebird.settle(items.map(item => myfs.statAsync(item)
.then(stat => {
if (stat.isFile()) {
return [item];
} else if (stat.isDirectory()) {
return getItems(item);
}
})))
.then(getFulfilledValues)
.then(result => {
return [].concat(...result);
});
}
function readFiles(files) {
return bluebird.settle(files.map(file => {
return myfs.readFileAsync(file);
}))
.then(getFulfilledValues);
}
function getFulfilledValues(results) {
return results
.filter(result => result.isFulfilled())
.map(result => result.value());
}
main(__dirname)
.then(console.log)
.catch(console.error);
Всем хорошего говнокода, а мне срача в каментах, покажите дедушке, что он отстал от времени и нехрена не шарит без очков