Чи є ще причини використовувати бібліотеки з обіцянками, такі як Q або BlueBird, тепер, коли ми обіцяємо ES6? [зачинено]


228

Після того, як Node.js додав нативну підтримку обіцянок, чи все ж є причини використовувати такі бібліотеки, як Q або BlueBird?

Наприклад, якщо ви починаєте новий проект, і припустимо, що в цьому проекті у вас немає залежностей, якими користуються ці бібліотеки, чи можна сказати, що справді більше причин використовувати такі бібліотеки немає?


4
рідні обіцянки мають дуже дуже основні риси. Бібліотеки, такі як Q або Bluebird, додають ще купу. Якщо вам потрібні ці функції, використовуйте ці бібліотеки.
gman

7
Я відредагував заголовок, щоб зменшити кількість "необхідності" та більше про "причини все-таки використовувати бібліотеки з обіцянками". На це питання можна відповісти, насамперед, надаючи факти, а не думку. Це слід відкрити, оскільки на нього можна відповісти, надаючи факти, а не насамперед думку. Дивіться відповідь нижче як демонстрацію цього.
jfriend00

11
@JaromandaX - Будь ласка, подумайте про повторне відкриття тепер, коли заголовок і питання було розроблено, щоб дізнатися більше про те, чому можна використовувати бібліотеку обіцянок, а не про те, чи потрібно "використовувати" бібліотеку обіцянок. На мою думку, на це питання можна відповісти, надаючи факти, а не насамперед думки - див. Відповідь нижче як демонстрацію цього.
jfriend00

6
Це питання після редагування заголовка та його прийнята відповідь не ґрунтуються на думці.
макс

7
Домовились. Це абсолютно актуальне питання в його нинішній формі. Я висунувся на повторне відкриття.
Жуль

Відповіді:


367

Стара приказка говорить про те, що ви повинні вибрати правильний інструмент для роботи. ES6 обіцянки забезпечують основи. Якщо все, що вам колись хочеться чи потрібно, - це основи, то це повинно / могло б працювати для вас просто чудово. Але в інструментарі є більше інструментів, ніж просто основи, і є ситуації, коли ці додаткові інструменти дуже корисні. І я б заперечував, що в ES6-обіцянках навіть відсутні деякі основи на зразок обізнаності, які є корисними майже в кожному проекті node.js.

Я найбільше знайомий із бібліотекою обіцянок Bluebird, тому я буду говорити здебільшого зі свого досвіду роботи з цією бібліотекою.

Отже, ось мої головні 6 причин використовувати більш спроможну бібліотеку Promise

  1. Необіцяні інтерфейси асинхронізації - .promisify()і .promisifyAll()неймовірно корисні для обробки всіх тих асинхронних інтерфейсів, які все ще потребують простого зворотного виклику та ще не повертають обіцянок - один рядок коду створює обіцяну версію всього інтерфейсу.

  2. Швидше - Bluebird набагато швидше, ніж рідні обіцянки в більшості середовищ.

  3. Послідовність ітерації масиву асинхронізації - Promise.mapSeries()або Promise.reduce()дозволяють вам повторювати масив, викликаючи операцію асинхронізації на кожному елементі, але послідовність операцій асинхронізації, щоб вони відбувалися одна за одною, а не всі одночасно. Це можна зробити або тому, що цього вимагає цільовий сервер, або тому, що вам потрібно передати один результат наступному.

  4. Polyfill - якщо ви хочете використовувати обіцянки у старих версіях клієнтів браузера, вам все одно знадобиться поліфайл. Також може отримати здатний поліфіл. Оскільки node.js обіцяє ES6, вам не потрібна полізаповнення в node.js, але ви можете в браузері. Якщо ви кодуєте і сервер node.js, так і клієнта, може бути дуже корисним мати однакову бібліотеку обіцянок і функції в обох (простіше ділитися кодом, перемикатися в контексті між середовищами, використовувати загальні методи кодування для коду асинхронізації тощо). .).

  5. Інші корисні функції - Bluebird має Promise.map(), Promise.some(), Promise.any(), Promise.filter(), Promise.each()і Promise.props()всі з яких іноді зручно. Хоча ці операції можна виконувати за допомогою обіцянок ES6 та додаткового коду, Bluebird поставляється з цими операціями вже заздалегідь складеними та попередньо протестованими, так що простіше і менше коду для їх використання.

  6. Вбудовані попередження та сліди повного стека - Bluebird має ряд вбудованих попереджень, які попереджають вас про проблеми, які, ймовірно, неправильний код чи помилка. Наприклад, якщо ви викликаєте функцію, яка створює нову обіцянку всередині .then()обробника, не повертаючи цю обіцянку (щоб з'єднати її в поточний ланцюжок обіцянок), то в більшості випадків це випадкова помилка, і Bluebird попередить вас про це ефект. Інші вбудовані попередження Bluebird описані тут .

Ось докладніше про ці різні теми:

PromisifyAll

У будь-якому проекті node.js я негайно використовую Bluebird скрізь, оскільки я .promisifyAll()дуже багато використовую у стандартних node.js модулях, таких як fsмодуль.

Node.js сам по собі не забезпечує перспективний інтерфейс для вбудованих модулів, які не асинхронізують IO, як fs модуль. Отже, якщо ви хочете використовувати обіцянки з тими інтерфейсами, вам залишається або кодувати рукою обгортку обіцянок навколо кожної функції, яку ви використовуєте, або отримати бібліотеку, яка може зробити це для вас або не використовувати обіцянки.

Bluebird Promise.promisify()і Promise.promisifyAll()забезпечують автоматичне обгортання node.js, що викликає API асинхронних конвенцій для повернення обіцянок. Це надзвичайно корисно та економить час. Я ним користуюся постійно.

Ось приклад того, як це працює:

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));

fs.readFileAsync('somefile.text').then(function(data) {
   // do something with data here
});

Альтернативою було б вручну створити власну обгортку обіцянок для кожного fsAPI, який ви хочете використовувати:

const fs = require('fs');

function readFileAsync(file, options) {
    return new Promise(function(resolve, reject) {
        fs.readFile(file, options, function(err, data) {
            if (err) {
                reject(err);
            } else {
                 resolve(data);
            }
        });
    });
}

readFileAsync('somefile.text').then(function(data) {
   // do something with data here
});

І це потрібно робити вручну для кожної функції API, яку ви хочете використовувати. Це явно не має сенсу. Це код котла. Ви також можете отримати утиліту, яка робить це за вас. Bluebird's Promise.promisify()і Promise.promisifyAll()є такою утилітою.

Інші корисні функції

Ось деякі функції Bluebird, які я конкретно вважаю корисними (наведено кілька прикладів коду нижче, як вони можуть зберегти код або розвинути швидкість):

Promise.promisify()
Promise.promisifyAll()
Promise.map()
Promise.reduce()
Promise.mapSeries()
Promise.delay()

Окрім корисної функції, Promise.map()також підтримує параметр одночасності, який дозволяє вам вказати, скільки операцій слід дозволити одночасно виконувати, що особливо корисно, коли у вас є багато чого робити, але не можна перевантажувати деякі зовні ресурс.

Деякі з них можна назвати автономними та використовувати їх за обіцянкою, яка сама вирішує ітерабельний режим, що може заощадити багато коду.


Поліфіл

У проекті веб-переглядача, як правило, ви все ще хочете підтримувати деякі веб-переглядачі, які не підтримують Promise, ви все одно потребуєте полізаповнення. Якщо ви також використовуєте jQuery, іноді ви можете просто використовувати підтримку обіцянок, вбудовану в jQuery (хоча вона дещо болісно нестандартна, можливо, зафіксована в jQuery 3.0), але якщо проект передбачає будь-яку значущу активність асинхронізації, я вважаю розширені функції в Bluebird дуже корисні.


Швидше

Також варто зазначити, що обіцянки Bluebird виявляються значно швидшими, ніж обіцянки, вбудовані у V8. Дивіться цей пост, щоб отримати більше обговорень на цю тему.


Велика річ Node.js відсутня

Що б змусити мене використовувати Bluebird менше в розробці node.js, було б, якби node.js вбудований у функцію промотивації, щоб ви могли зробити щось подібне:

const fs = requirep('fs');

fs.readFileAsync('somefile.text').then(function(data) {
   // do something with data here
});

Або просто пропонуйте вже обіцяні методи як частину вбудованих модулів.

До цього часу я роблю це з Bluebird:

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));

fs.readFileAsync('somefile.text').then(function(data) {
   // do something with data here
});

Здається, трохи дивним є підтримка ES6 для обіцянок, вбудована в node.js, і жоден із вбудованих модулів не повертає обіцянок. Це потрібно розібратися в node.js. До цього часу я використовую Bluebird для розмежування цілих бібліотек. Таким чином, здається, що обіцянки зараз приблизно на 20% реалізовані в node.js, оскільки жоден із вбудованих модулів не дозволяє вам використовувати обіцянки, не спочатку вручну запускаючи їх.


Приклади

Ось приклад прості обіцянки та розцінки Bluebird та паралельне Promise.map()читання набору файлів та повідомлення про завершення з усіма даними:

Прості обіцянки

const files = ["file1.txt", "fileA.txt", "fileB.txt"];
const fs = require('fs');

// make promise version of fs.readFile()
function fsReadFileP(file, options) {
    return new Promise(function(resolve, reject) {
        fs.readFile(file, options, function(err, data) {
            if (err) return reject(err);
            resolve(data);
        });
    });
}


Promise.all(files.map(fsReadFileP)).then(function(results) {
    // files data in results Array
}, function(err) {
    // error here
});

Синій птах Promise.map()іPromise.promisifyAll()

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
const files = ["file1.txt", "fileA.txt", "fileB.txt"];

Promise.map(files, fs.readFileAsync).then(function(results) {
    // files data in results Array
}, function(err) {
    // error here
});

Ось приклад прості обіцянки проти Bluebird, які підкреслюються, і Promise.map()читаючи купу URL-адрес з віддаленого хоста, де ви можете прочитати не більше 4 одночасно, але хочете зберегти стільки запитів паралельно, скільки дозволено:

Прості обіцянки JS

const request = require('request');
const urls = [url1, url2, url3, url4, url5, ....];

// make promisified version of request.get()
function requestGetP(url) {
    return new Promise(function(resolve, reject) {
        request.get(url, function(err, data) {
            if (err) return reject(err);
            resolve(data);
        });
    });
}

function getURLs(urlArray, concurrentLimit) {
    var numInFlight = 0;
    var index = 0;
    var results = new Array(urlArray.length);
    return new Promise(function(resolve, reject) {
        function next() {
            // load more until concurrentLimit is reached or until we got to the last one
            while (numInFlight < concurrentLimit && index < urlArray.length) {
                (function(i) {
                    requestGetP(urlArray[index++]).then(function(data) {
                        --numInFlight;
                        results[i] = data;
                        next();
                    }, function(err) {
                        reject(err);
                    });
                    ++numInFlight;
                })(index);
            }
            // since we always call next() upon completion of a request, we can test here
            // to see if there was nothing left to do or finish
            if (numInFlight === 0 && index === urlArray.length) {
                resolve(results);
            }
        }
        next();
    });
}

Синій птах обіцяє

const Promise = require('bluebird');
const request = Promise.promisifyAll(require('request'));
const urls = [url1, url2, url3, url4, url5, ....];

Promise.map(urls, request.getAsync, {concurrency: 4}).then(function(results) {
    // urls fetched in order in results Array
}, function(err) {
    // error here
});

хоча це болісно нестандартне в якому - то сенсі - Вони стверджують , що вони є «Promises / A + сумісний» зараз :) - blog.jquery.com/2016/01/14/jquery-3-0-beta-released
thefourtheye

1
@thefourtheye - Так, я знаю, що вони працювали над сумісністю Promise / A + в 3.0. Але це все ще в бета-версії. Якщо він відповідає обіцянці (призначений каламбур), це може усунути деякі причини використання зовнішньої бібліотеки обіцянок у веб-переглядачі JS, якщо ви вже використовували jQuery. Він все ще не матиме всіх корисних функцій, які робить Bluebird, і я був би надзвичайно здивований, якщо він відповідає результатам роботи Bluebird, тому у Bluebird все ще є місце поряд з майбутнім jQuery в деяких випадках. У будь-якому випадку, питання ОП, як правило, стосується node.js.
jfriend00

1
Існує невелика помилка в останньому прикладі коду: return new Promise(function(resolve, rejct). Повинно бути:reject
Себастьян Мушинський

7
Node.js насправді є util.promisifyзараз, хоча прямого promisifyAllеквівалента немає .
nyuszika7h

1
@Aurast - Так, v11 піклується про це fs, але все ж деякі інші причини використовувати Bluebird (моїм улюбленим моїм concurrencyваріантом є варіант Promise.map()), щоб не перевантажувати цільовий сервіс, на який потрібно зробити купу паралельних запитів. Крім того, ще багато інших необіцяних інтерфейсів для використання PromimfyAll з Bluebird. Але, повільно, причини одразу захопити Bluebird у кожному новому проекті згасають, оскільки node.js сам покращує свою вбудовану підтримку обіцянок.
jfriend00
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.