Vow: самые быстрые промисы

    Хочу предоставить вашему вниманию библиотеку Vow, которую написал мой коллега Дмитрий Филатов dfilatov.

    Библиотека реализует Promises/A+, работает очень быстро и требует малого объема памяти. По тестам производительности сильно опережает Q, но при этом сохраняет асинхронную манеру работы.

    Работа с Vow выглядит так же просто, как работа с Q. Из недостатков (по сравнению с Q) можно лишь выделить отсутствие progress.

    Пример кода с использованием Vow:
    function readFile(filename, encoding) {
        var promise = Vow.promise();
        fs.readFile(filename, encoding, function(err, data) {
            if (err) return promise.reject(err);
            promise.fulfill(data);
        });
        return promise;
    }
    Vow.all([readFile('test1.txt', 'utf8'), readFile('test2.txt', 'utf8')]).then(function(results) {
        console.log(results.join('\n'));
    });
    

    Бенчмарк, который отражает, насколько Q медленная библиотека (тестируется создание последовательных промисов):
    время операций в секунду
    Q 54.891ms 18
    When 3.484ms 287
    Vow 1.158ms 864

    Также для Vow реализована библиотека работы с файловой системой: vow-fs: https://github.com/dfilatov/vow-fs.

    NPM Пакет: vow
    Репозиторий: https://github.com/dfilatov/jspromise
    Поделиться публикацией

    Похожие публикации

    Комментарии 19

      +1
      А с этим добром сравнивали?
      habrahabr.ru/post/168929/
        +1
        Нет. Но вы можете сравнить и поделиться результатами.
        +2
        Название созвучно с vows: github.com/cloudhead/vows
          +2
          Расскажите где были самые узкие места? :-)
            +3
            Не разработчик, но расскажу:
            Самая дорогая операция — nextTick, но тут все Promise/A+ равны. Поэтому даже самый плохой Promise/A будет быстрее любого Promise/A+ в синтетических тестах
            Q — адов функциональный кошмар, генерация функций вместо использования прототипов — долго, не оптимально, затратно по памяти
            Vow — чистые прототипы, нет генераций функций. Поэтому предельно дешево, JIT-friendly. Но нужно немного допилить, чтобы было совсем хорошо.
              0
              а почему вы считайте, что такая архитектура javascript'a (функция, в ней куча прототипов, а потом инициализация) JIT-friendly? Можно какую-то инфу по этому поводу?
                0
                Как быстро истекает время на редактирование), добавлю:
                Не в сравнении, конечно, с генерацией функций выше (это понятно, что быстрее будет), а с более классическим подходом, когда функции это методы в объекте, а инициализация просто собирает их по необходимости.
                  0
                  Во многих виртуальных машинах JavaScript к функции при ее инициализации добавляется специальный объект Scope, который описывает область видимости функции и чаще всего он инициализируется сразу же — в него попадают ссылки на все переменные контекстов до которых может достучаться функция. Если переменных много и контекст многоуровневый и функции генерируются очень часто (типичный функциональный подход), то это дорого. Полюс ко всему JIT компилятору нужно каждый раз оптимизировать каждую функцию.

                  Методы прототипа инициализируются только один раз и делегируют свое использование инстансу.
            +3
            Вот бы технических деталей, а то эти сухие тесты.
              0
              А результат для Q в таблице приведен с опцией «Q.longStackJumpLimit = 0» или без?
                0
                Насколько я помню, там в тесте Q была версии 0.8, в ней вроде еще не было этой опции.
                Сейчас померял с Q 0.9 — там отключение этой опции дает существенный буст, она становится даже чуть быстрее When (примерно на 10-15%).
                +2
                А deferred? Не паранои ради, а для порядка.

                Еще есть один важный хэлпер — Q.nfbind и Q.nfapply, Q.nfcall с ним. С ним можно просто сделать вот:
                var readFile = Q.nfbind(fs.readFile);

                Понятно, что есть vow-fs, но nfbind нужен не только для fs.

                Еще есть достаточно частый кейс, когда разработчик ожадает, что функции resolve и reject забиндены к контексту при рождении. И можно делать вот так:
                var timeout = function (ms) {
                    var dfd = $.Deferred();
                    
                    setTimeout(dfd.resolve, ms);
                    
                    return dfd.promise();
                };
                
                // VS
                
                var timeout = function (ms) {
                    var promise = Vow.promise();
                    
                    // .bind :(
                    setTimeout(promise.fulfill.bind(promise), ms);
                    
                    return promise;
                };
                

                Понятно, что vow выигрывает на экономии в бинде, но это уродует код.
                  0
                  согласен с Димой, что эти функции правильнее вынести в дополнительный модуль, а не в ядро
                  0
                  When в тесте версии 2.0.0? Кстати, добавляйтесь сюда github.com/cujojs/promise-perf-tests
                    0
                    Да, When в этом тесте именно версии 2.0.0
                    0
                    dfilatov, замерь еще использование памяти и GC на каком-нибудь долгоиграющем тесте.
                      0
                      Можешь посоветовать какую-нибудь тулзу для автоматизации этого?
                      0
                      mean time ops/sec
                      Q 98.002ms 10
                      When 16.895ms 59
                      Vow 12.302ms 81
                      fibers/promise 12.738ms 79

                      и это в свете того, что в последнем случае есть классная функция wait, приостанавливающая текущее волокно в ожидании ответа

                      benchmarks/comparison.js | 28 ++++++++++++++++++++++++++++
                      1 file changed, 28 insertions(+)

                      diff --git a/benchmarks/comparison.js b/benchmarks/comparison.js
                      index 8520d17..7bcf306 100644
                      — a/benchmarks/comparison.js
                      +++ b/benchmarks/comparison.js
                      @@ -6,6 +6,34 @@ var cliff = require('cliff'),
                      Q = require('q'),
                      When = require('when'),
                      tests = {
                      + 'fibers/promise': function(deferred){
                      + var Fiber = require('fibers');
                      + var Future= require('fibers/future')
                      +
                      + var toResolve = [],
                      + topPromises = [];
                      +
                      + Object.keys(data).forEach(function(key) {
                      + var promises= data[key].map(function(val) {
                      + var promise = new Future();
                      + toResolve.push({ promise: promise, val: val });
                      + return promise;
                      + })
                      + topPromises.push(promises);
                      + });
                      +
                      + Fiber( function(){
                      + var result= topPromises.map(function(promises){
                      + return Future.wait( promises)
                      + })
                      + deferred.resolve()
                      + }).run()
                      +
                      + toResolve.forEach(function(obj) {
                      + obj.promise.return(obj.val);
                      + });
                      + },
                      +
                      'Q': function(deferred) {
                      var toResolve = [],
                      topPromises = [];
                        0
                        Библиотека изменила синтаксис, стоит сделать апдейт поста.

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                        Самое читаемое