ФорумПрограммированиеJavaScript → пирамида смерти

пирамида смерти

  • Trej Gun

    Сообщения: 5305 Репутация: N Группа: в ухо

    Spritz 21 декабря 2015 г. 12:34

    Бабулины сказки

    Тут все такие ахуенные, могут настроить по мануалу 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);
    

    Всем хорошего говнокода, а мне срача в каментах, покажите дедушке, что он отстал от времени и нехрена не шарит без очков

  • phpdude

    Сообщения: 26646 Репутация: N Группа: в ухо

    Spritz 21 декабря 2015 г. 12:58, спустя 23 минуты 50 секунд

    да все равно полная асинхронность это пизда коду. согласен, даже не очем говорить

    Сапожник без сапог
  • Trej Gun

    Сообщения: 5305 Репутация: N Группа: в ухо

    Spritz 21 декабря 2015 г. 13:01, спустя 2 минуты 23 секунды

    я ничего не понял, но ты такой категоричный что я согласен!

  • phpdude

    Сообщения: 26646 Репутация: N Группа: в ухо

    Spritz 21 декабря 2015 г. 13:03, спустя 1 минуту 53 секунды

    я ничего не понял, но ты такой категоричный что я согласен!

    @CTAPbIu_MABP, мне больше любопытно как на полной асинхронности предлагают реализовывать eventloop какой нить )) while(1) {}? :D

    Сапожник без сапог
  • master

    Сообщения: 3244 Репутация: N Группа: Джедаи

    Spritz 21 декабря 2015 г. 13:07, спустя 1 минуту 36 секунд

    я вот абсолютно искренне не понимаю, зачем использовать 4 пробела когда можно 2. везде использую 2 (кроме петона, где есть PEP-8, но я на петоне не пишу)

    не всё полезно, что в swap полезло
  • phpdude

    Сообщения: 26646 Репутация: N Группа: в ухо

    Spritz 21 декабря 2015 г. 13:10, спустя 2 минуты 19 секунд

    я вот абсолютно искренне не понимаю, зачем использовать 4 пробела когда можно 2. везде использую 2 (кроме петона, где есть PEP-8, но я на петоне не пишу)

    @master, это конечно решит всю проблему )

    Сапожник без сапог
  • master

    Сообщения: 3244 Репутация: N Группа: Джедаи

    Spritz 21 декабря 2015 г. 13:10, спустя 40 секунд

    в остальном согласен, и про колбэки и про вот это
    for (var i = 0, j = files.length; i < j; i++) { console.log(files[i]); }
    в 21м-то веке, когда даже в пхп есть форич

    Спустя 120 сек.

    @master, это конечно решит всю проблему )

    @phpdude, по крайней мере не создаст дополнительную. потому что имхо 4 пробела - это порождение проблемы и есть, начиная со срача "чем делать отступы - пробелами или табами"

    не всё полезно, что в swap полезло
  • Trej Gun

    Сообщения: 5305 Репутация: N Группа: в ухо

    Spritz 21 декабря 2015 г. 13:13, спустя 2 минуты 27 секунд

    @master, я с удовольствием пофлеймлю в теме про пробелы и темный фон в редакторе кода, но не в этой теме)

  • phpdude

    Сообщения: 26646 Репутация: N Группа: в ухо

    Spritz 21 декабря 2015 г. 13:13, спустя 18 секунд

    @master, я звездочками отделяю код

    Сапожник без сапог
  • Trej Gun

    Сообщения: 5305 Репутация: N Группа: в ухо
  • vasa_c

    Сообщения: 3131 Репутация: N Группа: в ухо

    Spritz 21 декабря 2015 г. 13:23, спустя 9 минут 36 секунд

    fs.readdir(__dirname, function (err, files) {
        if (err) {
            console.log(err);
        } else {
            for (var i = 0, j = files.length; i &lt; j; i++) {
                var file = path.join(__dirname, files[i]);
                fs.stat(file, function (err, stats) {
                    if (err) {
                        console.log(err);
                    } else if (stats.isFile()) {
                        fs.readFile(file, function (err, data) {
                            if (err) {
                                console.log(err);
                            } else {
                                buffers.push(data);
                            }
                        });
                    }
                });
            }
        }
    });
    

    А ещё теперь можно заменить "function" на всякую поебень типа () => после чего JS по читаемости обгонит perl.

  • kostyl

    Сообщения: 5210 Репутация: N Группа: Джедаи

    Spritz 21 декабря 2015 г. 13:26, спустя 2 минуты 27 секунд

    для многих задач, мне кажется, достаточно остановится на промисах и всё, например, чтобы джунам было понятнее потом ...

  • Trej Gun

    Сообщения: 5305 Репутация: N Группа: в ухо

    Spritz 21 декабря 2015 г. 13:35, спустя 8 минут 43 секунды

    @vasa_c, а ты уже ждешь pipeline оператор |>

    
    fs.readdir(__dirname, (err, files) => {
        if (err) {
            err |> console.log;
        } else {
            for (var i = 0, j = files.length; i < j; i++) {
                var file = [__dirname, files[i]] |> path.join;
                fs.stat(file, (err, stats) => {
                    if (err) {
                        err |> console.log;
                    } else if (stats.isFile()) {
                        fs.readFile(file, (err, data) => {
                            if (err) {
                                err |> console.log;
                            } else {
                                data |> buffers.push;
                            }
                        });
                    }
                });
            }
        }
    });
    

    уже доступно кстати, Implement pipeline operator plugin · mindeavor/babel@415b56b [github.com]

  • master

    Сообщения: 3244 Репутация: N Группа: Джедаи

    Spritz 21 декабря 2015 г. 13:35, спустя 43 секунды

    ох

    не всё полезно, что в swap полезло

Пожалуйста, авторизуйтесь, чтобы написать комментарий!