Что такое простая реализация async.водопад?

Я использую некоторые функции из асинхронный библиотека, и хочу убедиться, что я понимаю, как они делают вещи внутри; однако я застрял на async.waterfall (реализация здесь). Фактическая реализация использует другие функции из библиотеки, и без большого опыта мне трудно следовать.

может кто-то, не заботясь об оптимизации, предоставить очень просто реализация это обеспечивает функциональность водопада? Вероятно, что-то сравнимое с ответ.

С документы, описание водопада:

запускает массив задач функций последовательно, каждая из которых результаты в массив. Однако, если какая-либо из задач проходит ошибка собственного обратного вызова, следующая функция не выполняется, и основной обратный вызов немедленно вызывается с помощью ошибка.

пример:

async.waterfall([
    function(callback) {
        callback(null, 'one', 'two');
    },
    function(arg1, arg2, callback) {
      // arg1 now equals 'one' and arg2 now equals 'two'
        callback(null, 'three');
    },
    function(arg1, callback) {
        // arg1 now equals 'three'
        callback(null, 'done');
    }
], function (err, result) {
    // result now equals 'done'    
});

2 ответов


Ну, вот простая реализация для цепных функций, выстраивая их в очередь.

прежде всего, функция:

function waterfall(arr, cb){} // takes an array and a callback on completion

теперь нам нужно отслеживать массив и перебирать его:

function waterfall(arr, cb){
    var fns = arr.slice(); // make a copy
}

давайте начнем с обработки переданного и пустого массива, добавив дополнительный параметр, чтобы мы могли передавать результаты под названием result:

function waterfall(arr, cb, result){ // result is the initial result
    var fns = arr.slice(); // make a copy
    if(fns.length === 0){
        process.nextTick(function(){ // don't cause race conditions
            cb(null, result); // we're done, nothing more to do
        });
    }
}

что хорошо:

waterfall([], function(err, data){
    console.log("Done!");
});

теперь давайте справимся с тем, что у нас есть в:

function waterfall(arr, cb, result){ // result is the initial result
    var fns = arr.slice(1); // make a copy, apart from the first element
    if(!arr[0]){ // if there is nothing in the first position
        process.nextTick(function(){ // don't cause race conditions
            cb(null, result); // we're done, nothing more to do
        });
        return;
    }
    var first = arr[0]; // get the first function
    first(function(err, data){ // invoke it
         // when it is done
         if(err) return cb(err); // early error, terminate entire call
         // perform the same call, but without the first function
         // and with its result as the result
         waterfall(fns, cb, data); 
    });
}

и это все! Мы преодолеваем тот факт, что мы не можем цикл с обратными вызовами, используя рекурсию в основном. вот скрипка иллюстрируя его.

стоит отметить, что если бы мы реализовывали его с обещаниями, мы могли бы использовать цикл for.


для тех, кто любит держать его коротким:

function waterfall(fn, done){
   fn.length ? fn.shift()(function(err){ err ? done(err) : waterfall(fn, done) }) :  done();
}