Включение задержки между каждым индивидуальным вызовом в цепочке обещаний API-вызовов [duplicate]

Рассмотрим следующий код, который читает массив файлов последовательным / последовательным образом. readFiles возвращает обещание, которое разрешается только после того, как все файлы были прочитаны в последовательности.

var Q = require("q");

var readFile = function(file) {
  ... // Returns a promise.
};

var readFiles = function(files) {
  var deferred = Q.defer();

  var readSequential = function(index) {
    if (index >= files.length) {
      deferred.resolve();
    } else {
      readFile(files[index]).then(function() {
        readSequential(index + 1);
      });
    }
  };

  readSequential(0); // Start!

  return deferred.promise;
};

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

Первоначально я попытался использовать Q.all, но это вызвало все readFile вызывает одновременное выполнение, чего я не хочу:

var readFiles = function(files) {
  return Q.all(files.map(function(file) {
    return readFile(file);
  }));
};
161
задан 25 December 2014 в 15:24

19 ответов

Используйте Array.prototype.reduce и не забудьте обернуть свои обещания функцией, иначе они уже будут запущены!

// array of Promise providers

const providers = [
  function(){
     return Promise.resolve(1);
  },
  function(){
     return Promise.resolve(2);
  },
  function(){
     return Promise.resolve(3);
  }
]


const seed = Promise.resolve(null);

const inSeries = function(providers){
  return providers.reduce(function(a,b){
      return a.then(b);
  }, seed);
};

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

2
ответ дан 15 August 2018 в 15:37

Мой ответ основан на https://stackoverflow.com/a/31070150/7542429.

Promise.series = function series(arrayOfPromises) {
    var results = [];
    return arrayOfPromises.reduce(function(seriesPromise, promise) {
      return seriesPromise.then(function() {
        return promise
        .then(function(result) {
          results.push(result);
        });
      });
    }, Promise.resolve())
    .then(function() {
      return results;
    });
  };

Это решение возвращает результаты в виде массива вроде Promise.all ().

Использование:

Promise.series([array of promises])
.then(function(results) { 
  // do stuff with results here
});
0
ответ дан 15 August 2018 в 15:37

Я создал этот простой метод для объекта Promise:

Создает и добавляет метод Promise.sequence к объекту Promise

Promise.sequence = function (chain) {
    var results = [];
    var entries = chain;
    if (entries.entries) entries = entries.entries();
    return new Promise(function (yes, no) {
        var next = function () {
            var entry = entries.next();
            if(entry.done) yes(results);
            else {
                results.push(entry.value[1]().then(next, function() { no(results); } ));
            }
        };
        next();
    });
};

Использование:

var todo = [];

todo.push(firstPromise);
if (someCriterium) todo.push(optionalPromise);
todo.push(lastPromise);

// Invoking them
Promise.sequence(todo)
    .then(function(results) {}, function(results) {});

Лучшее в этом расширении объекта Promise заключается в том, что оно соответствует стилю обещаний. Promise.all и Promise.sequence вызываются одинаково, но имеют различную семантику.

Внимание

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

2
ответ дан 15 August 2018 в 15:37
  • 1
    Нет, вы не можете сравнить Promise.all и ваш Promise.sequence. Один делает итерабельность обещаний, другой принимает множество функций, которые возвращают обещания. – Bergi 29 July 2015 в 17:14
  • 2
    Кстати, я бы рекомендовал избежать конструктора-конструктора обещания – Bergi 29 July 2015 в 17:15
  • 3
    Не знал, что для этого потребовался итератор. Должно быть достаточно легко переписать его, хотя. Не могли бы вы объяснить, почему это антипаттерн-конструктор обещаний? Я прочитал ваше сообщение здесь: stackoverflow.com/a/25569299/1667011 – frodeborli 30 July 2015 в 12:38
  • 4
    @Bergi Я обновил код для поддержки итераторов. Я до сих пор не вижу, что это антипаттерн. Антипаттеры обычно должны рассматриваться как рекомендации, чтобы избежать ошибок при кодировании, и совершенно правильно создавать (библиотечные) функции, которые нарушают эти рекомендации. – frodeborli 30 July 2015 в 13:08
  • 5
    Да, если вы считаете это библиотечной функцией, все в порядке, но все же в этом случае reduce, как в ответе Бенджамина, намного проще. – Bergi 30 July 2015 в 14:53

Мне действительно понравился ответ @ joelnet, но для меня этот стиль кодирования немного сложнее переварить, поэтому я потратил пару дней, пытаясь понять, как я буду выражать то же самое решение более читаемым образом и это мой прием, просто с другим синтаксисом и некоторыми комментариями.

// first take your work
const urls = ['/url1', '/url2', '/url3', '/url4']

// next convert each item to a function that returns a promise
const functions = urls.map((url) => {
  // For every url we return a new function
  return () => {
    return new Promise((resolve) => {
      // random wait in milliseconds
      const randomWait = parseInt((Math.random() * 1000),10)
      console.log('waiting to resolve in ms', randomWait)
      setTimeout(()=>resolve({randomWait, url}),randomWait)
    })
  }
})


const promiseReduce = (acc, next) => {
  // we wait for the accumulator to resolve it's promise
  return acc.then((accResult) => {
    // and then we return a new promise that will become
    // the new value for the accumulator
    return next().then((nextResult) => {
      // that eventually will resolve to a new array containing
      // the value of the two promises
      return accResult.concat(nextResult)
    })
  })
};
// the accumulator will always be a promise that resolves to an array
const accumulator = Promise.resolve([])

// we call reduce with the reduce function and the accumulator initial value
functions.reduce(promiseReduce, accumulator)
  .then((result) => {
    // let's display the final value here
    console.log('=== The final result ===')
    console.log(result)
  })
1
ответ дан 15 August 2018 в 15:37

Это небольшое изменение другого ответа выше. Использование собственных обещаний:

function inSequence(tasks) {
    return tasks.reduce((p, task) => p.then(task), Promise.resolve())
}

Объяснение

Если у вас есть эти задачи [t1, t2, t3], то приведенное выше эквивалентно Promise.resolve().then(t1).then(t2).then(t3). Это поведение уменьшения.

Пояснение

Сначала вам нужно создать список задач! Задача - это функция, которая не принимает аргументов. Если вам нужно передать аргументы своей функции, используйте bind или другие методы для создания задачи. Например:

var tasks = files.map(file => processFile.bind(null, file))
inSequence(tasks).then(...)
4
ответ дан 15 August 2018 в 15:37

Ваш подход не плох, но у него есть две проблемы: он проглатывает ошибки, и он использует Explicit Construction Antipattern.

Вы можете решить обе эти проблемы и сделать код чистым, в то время как все еще используя одну и ту же общую стратегию:

var Q = require("q");

var readFile = function(file) {
  ... // Returns a promise.
};

var readFiles = function(files) {
  var readSequential = function(index) {
    if (index < files.length) {
      return readFile(files[index]).then(function() {
        return readSequential(index + 1);
      });
    }
  };

  // using Promise.resolve() here in case files.length is 0
  return Promise.resolve(readSequential(0)); // Start!
};
0
ответ дан 15 August 2018 в 15:37

Этот вопрос старый, но мы живем в мире ES6 и функциональном JavaScript, поэтому давайте посмотрим, как мы можем улучшить.

Поскольку обещания выполняются немедленно, мы не можем просто создать массив обещаний , все они будут стрелять параллельно.

Вместо этого нам нужно создать массив функций, который возвращает обещание. Каждая функция будет выполняться последовательно, а затем запускает обещание внутри.

Мы можем решить это несколькими способами, но мой любимый способ - использовать reduce.

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

Суть этой функции заключается в использовании reduce, начиная с начального значения из Promise.resolve([]) или обещание, содержащее пустой массив.

Это обещание будет передано в метод reduce как promise. Это ключ к последовательному объединению каждого обещания. Следующее обещание выполнить func, а когда then срабатывает, результаты конкатенируются и затем возвращается обещание, выполняя цикл reduce со следующей функцией обещания.

Как только все обещания

Пример ES6 (один лайнер)

/*
 * serial executes Promises sequentially.
 * @param {funcs} An array of funcs that return promises.
 * @example
 * const urls = ['/url1', '/url2', '/url3']
 * serial(urls.map(url => () => $.ajax(url)))
 *     .then(console.log.bind(console))
 */
const serial = funcs =>
    funcs.reduce((promise, func) =>
        promise.then(result => func().then(Array.prototype.concat.bind(result))), Promise.resolve([]))

ES6 Пример (с разбивкой)

// broken down to for easier understanding

const concat = list => Array.prototype.concat.bind(list)
const promiseConcat = f => x => f().then(concat(x))
const promiseReduce = (acc, x) => acc.then(promiseConcat(x))
/*
 * serial executes Promises sequentially.
 * @param {funcs} An array of funcs that return promises.
 * @example
 * const urls = ['/url1', '/url2', '/url3']
 * serial(urls.map(url => () => $.ajax(url)))
 *     .then(console.log.bind(console))
 */
const serial = funcs => funcs.reduce(promiseReduce, Promise.resolve([]))

Использование:

// first take your work
const urls = ['/url1', '/url2', '/url3', '/url4']

// next convert each item to a function that returns a promise
const funcs = urls.map(url => () => $.ajax(url))

// execute them serially
serial(funcs)
    .then(console.log.bind(console))
39
ответ дан 15 August 2018 в 15:37
  • 1
    очень хорошо, спасибо, Array.prototype.concat.bind(result) является той частью, которую я отсутствовал, приходилось делать толкание к результатам вручную, которое работало, но было менее прохладно – zavr 3 February 2017 в 14:32
  • 2
    Поскольку мы все о современных JS, я считаю, что утверждение console.log.bind(console) в вашем последнем примере теперь обычно не нужно. В эти дни вы можете просто пройти console.log. Например. [F3]. Протестировано на текущих узлах и Chrome. – Molomby 1 August 2017 в 07:41
  • 3
    Это было немного сложно обернуть вокруг меня, но сокращение по существу делает это правильно? [F1] – danecando 30 May 2018 в 09:59
  • 4
    @danecando, да, это выглядит правильно. Вы также можете отказаться от Promise.resolve в возврате, любые возвращаемые значения будут автоматически разрешены, если вы не назовете Promise.reject на них. – joelnet 1 June 2018 в 02:47

Я использую следующий код для расширения объекта Promise. Он обрабатывает отказ от обещаний и возвращает массив результатов

Код

/*
    Runs tasks in sequence and resolves a promise upon finish

    tasks: an array of functions that return a promise upon call.
    parameters: an array of arrays corresponding to the parameters to be passed on each function call.
    context: Object to use as context to call each function. (The 'this' keyword that may be used inside the function definition)
*/
Promise.sequence = function(tasks, parameters = [], context = null) {
    return new Promise((resolve, reject)=>{

        var nextTask = tasks.splice(0,1)[0].apply(context, parameters[0]); //Dequeue and call the first task
        var output = new Array(tasks.length + 1);
        var errorFlag = false;

        tasks.forEach((task, index) => {
            nextTask = nextTask.then(r => {
                output[index] = r;
                return task.apply(context, parameters[index+1]);
            }, e=>{
                output[index] = e;
                errorFlag = true;
                return task.apply(context, parameters[index+1]);
            });
        });

        // Last task
        nextTask.then(r=>{
            output[output.length - 1] = r;
            if (errorFlag) reject(output); else resolve(output);
        })
        .catch(e=>{
            output[output.length - 1] = e;
            reject(output);
        });
    });
};

Код

function functionThatReturnsAPromise(n) {
    return new Promise((resolve, reject)=>{
        //Emulating real life delays, like a web request
        setTimeout(()=>{
            resolve(n);
        }, 1000);
    });
}

var arrayOfArguments = [['a'],['b'],['c'],['d']];
var arrayOfFunctions = (new Array(4)).fill(functionThatReturnsAPromise);


Promise.sequence(arrayOfFunctions, arrayOfArguments)
.then(console.log)
.catch(console.error);
1
ответ дан 15 August 2018 в 15:37

Самое лучшее решение, которое я смог выяснить, было с bluebird обещаниями. Вы можете просто сделать Promise.resolve(files).each(fs.readFileAsync);, который гарантирует, что обещания разрешаются последовательно в порядке.

4
ответ дан 15 August 2018 в 15:37
  • 1
    Еще лучше: Promise.each(filtes, fs.readFileAsync). Кстати, вам не нужно делать .bind(fs)? – Bergi 6 May 2015 в 20:16
  • 2
    Кажется, никто здесь не понимает разницу между массивом и последовательностью, что последний подразумевает неограниченный / динамический размер. – vitaly-t 16 October 2015 в 20:10

Мое предпочтительное решение:

function processArray(arr, fn) {
    return arr.reduce(
        (p, v) => p.then((a) => fn(v).then(r => a.concat([r]))),
        Promise.resolve([])
    );
}

Это не принципиально отличается от других, опубликованных здесь, но:

Применяет функцию к элементам последовательно. Решает массив массивов результатов. требуют async / await (поддержка по-прежнему весьма ограничена, около 2017) Использует функции стрелок; nice и concise

Пример использования:

const numbers = [0, 4, 20, 100];
const multiplyBy3 = (x) => new Promise(res => res(x * 3));

// Prints [ 0, 12, 60, 300 ]
processArray(numbers, multiplyBy3).then(console.log);

Проверено на разумные текущие версии Chrome (v59) и NodeJS (v8.1.2).

4
ответ дан 15 August 2018 в 15:37

Простая утилита для стандартного обещания Node.js:

function sequence(tasks, fn) {
    return tasks.reduce((promise, task) => promise.then(() => fn(task)), Promise.resolve());
}

UPDATE

items-prom - это готовый к использованию пакет NPM, который делает то же самое.

21
ответ дан 15 August 2018 в 15:37
  • 1
    Я хотел бы, чтобы это объяснялось более подробно. – Tyguy7 23 May 2017 в 01:21
  • 2
    Я представил вариант этого ответа с объяснением ниже. благодаря – Hai Phan 11 July 2017 в 01:22

На основании названия вопроса «Решение обещает одно за другим (т. е. в последовательности)?», мы могли бы понять, что ОП больше интересуется последовательной обработкой обещаний по урегулированию, чем последовательными вызовами как таковыми.

Этот ответ предлагается:

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

Если одновременные вызовы действительно не нужны, см. ответ Benjamin Gruenbaum, в котором подробно рассматриваются последовательные вызовы (и т. д.).

Если, однако, вы заинтересованы (для повышения производительности) в шаблонах, которые позволяют одновременные вызовы, за которыми следует последовательная обработка ответов, затем, пожалуйста, прочитайте.

Заманчиво думать, что вы должны использовать Promise.all(arr.map(fn)).then(fn) (как я уже много раз делал), или модный саунд Promise lib (особенно Bluebird's), однако (с учетом этой статьи) шаблон arr.map(fn).reduce(fn) выполнит эту работу с преимуществами:

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

Здесь он написан для Q.

var readFiles = function(files) {
    return files.map(readFile) //Make calls in parallel.
    .reduce(function(sequence, filePromise) {
        return sequence.then(function() {
            return filePromise;
        }).then(function(file) {
            //Do stuff with file ... in the correct sequence!
        }, function(error) {
            console.log(error); //optional
            return sequence;//skip-over-error. To stop-on-error, `return error` (jQuery), or `throw  error` (Promises/A+).
        });
    }, Q()).then(function() {
        // all done.
    });
};

per se

Ключевым моментом здесь является сокращение sequence обещание, в котором последовательности обработки обещаний readFile, но не их создания.

И как только вы впитаете это, возможно, слегка раздувая, когда вы понимаете, что этап .map() t действительно необходимо! Вся работа, параллельные вызовы плюс последовательная обработка в правильном порядке, может быть достигнута только с помощью reduce(), плюс дополнительное преимущество дальнейшей гибкости:

дает гибкость для пропусков, error или stop-on-error, в зависимости от того, что вы хотите с модемной строкой.

Здесь это снова для Q.

var readFiles = function(files) {
    return files.reduce(function(sequence, f) {
        var filePromise = readFile(f);//Make calls in parallel. To call sequentially, move this line down one.
        return sequence.then(function() {
            return filePromise;
        }).then(function(file) {
            //Do stuff with file ... in the correct sequence!
        }, function(error) {
            console.log(error); //optional
            return sequence;//Skip over any errors. To stop-on-error, `return error` (jQuery), or `throw  error` (Promises/A+).
        });
    }, Q()).then(function() {
        // all done.
    });
};

Это основной рисунок. Если вы хотите также предоставить данные (например, файлы или некоторые их преобразования) для вызывающего, вам понадобится мягкий вариант.

0
ответ дан 15 August 2018 в 15:37
  • 1
    Я не думаю, что это хорошая идея ответить на вопросы, противоречащие намерениям OPs ... – Bergi 9 September 2015 в 16:07
  • 2
    Эта вещь sequence.then(() => filePromise) является антипаттерном - она ​​не распространяет ошибки, как только они могут (и создает unhandledRejection в libs, которые их поддерживают). Вы скорее должны использовать Q.all([sequence, filePromise]) или $.when(sequence, filePromise). По общему признанию, это поведение может быть тем, что вы хотите, когда пытаетесь игнорировать или пропускать ошибки, но вы должны хотя бы упомянуть об этом как о недостатке. – Bergi 9 September 2015 в 16:10
  • 3
    @Bergi, я надеюсь, что OP войдет и будет судить о том, действительно ли это противоречит его намерениям или нет. Если нет, я удалю ответ, я думаю, пока я надеюсь, что оправдал свою позицию. Спасибо, что приняли это достаточно серьезно, чтобы обеспечить достойную обратную связь. Можете ли вы подробнее объяснить анти-шаблон или предоставить ссылку, пожалуйста? То же самое относится к статье, где я нашел базовый шаблон ? – Roamer-1888 9 September 2015 в 19:47
  • 4
    Да, третья версия его кода (то есть «как параллельная, так и последовательная») имеет ту же проблему. "Антипаттерн" требует сложной обработки ошибок и подвержен асинхронному подключению обработчиков, что вызывает события unhandledRejection. В Bluebird вы можете обойти это, используя sequence.return(filePromise), который имеет такое же поведение, но отлично обрабатывает отклонения. Я не знаю никаких ссылок, я только что придумал это - я не думаю, что шаблон «(анти)» имеет имя еще. – Bergi 9 September 2015 в 20:43
  • 5
    @Bergi, вы можете ясно видеть то, что я не могу :( Интересно, нужно ли где-то документировать этот новый анти-шаблон? – Roamer-1888 9 September 2015 в 23:04

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

function one_by_one(objects_array, iterator, callback) {
    var start_promise = objects_array.reduce(function (prom, object) {
        return prom.then(function () {
            return iterator(object);
        });
    }, Promise.resolve()); // initial
    if(callback){
        start_promise.then(callback);
    }else{
        return start_promise;
    }
}

Функция принимает 2 аргумента + 1 опционально. Первый аргумент - это массив, над которым мы будем работать. Второй аргумент - это сама задача, функция, которая возвращает обещание, следующая задача будет начата только тогда, когда это обещание будет разрешено. Третий аргумент - это обратный вызов для запуска, когда все задачи выполнены. Если обратный вызов не передан, функция возвращает обещание, которое оно создало, чтобы мы могли обработать конец.

Вот пример использования:

var filenames = ['1.jpg','2.jpg','3.jpg'];
var resize_task = function(filename){
    //return promise of async resizing with filename
};
one_by_one(filenames,resize_task );

Надеюсь, что это кому-то поможет ...

10
ответ дан 15 August 2018 в 15:37
  • 1
    Невероятное решение, это было лучшее из того, что я нашел почти за неделю, когда он пробовал ... Это очень хорошо объяснено, имеет логические внутренние имена, хороший пример (может быть, лучше), я могу называть его безопасным, как многие по мере необходимости, и включает в себя возможность установки обратных вызовов. просто NICE! (Просто изменил имя на то, что делает меня более понятным) .... РЕКОМЕНДАЦИЯ для других ... вы можете итерировать объект, используя «Object.keys ( myObject )» как ваш «object_array», – DavidTaubmann 23 February 2017 в 12:27
  • 2
    Спасибо за ваш комментарий! Я тоже не использую это имя, но я хотел сделать это более очевидным / простым здесь. – Salketer 23 February 2017 в 12:36

Для этого просто в ES6:

function(files) {

    // Create a new empty promise (don't do that with real people ;)
    var sequence = Promise.resolve();

    // Loop over each file, and add on a promise to the
    // end of the 'sequence' promise.
    files.forEach(function(file) {

      // Chain one computation onto the sequence
      sequence = sequence.then(function() {
        return performComputation(file);
      }).then(function(result) {
        doSomething(result) // Resolves for each file, one at a time.
      });

    })

    // This will resolve after the entire chain is resolved
    return sequence;
  }
34
ответ дан 15 August 2018 в 15:37
  • 1
    Он использует ES2015 Promises вместо библиотеки типа Q или bluebird, которые имеют некоторые специальные функции. – Shridhar Gupta 22 April 2016 в 21:37
  • 2
    Кажется, он использует подчеркивание. Вы можете упростить files.forEach, если файлы являются массивом. – Gustavo Rodrigues 25 July 2016 в 21:36
  • 3
    Ну ... это ES5. Путь ES6 будет for (file of files) {...}. – Gustavo Rodrigues 26 July 2016 в 23:40
  • 4
    Вы говорите, что вы не должны использовать Promise.resolve() для создания обещанного обещания в реальной жизни. Почему нет? Promise.resolve() кажется более чистым, чем new Promise(success => success()). – canac 10 January 2017 в 01:28
  • 5
    @canac Извините, это была просто шутка с игрой на словах («пустые обещания ..»). Определенно используйте Promise.resolve(); в вашем коде. – Shridhar Gupta 11 January 2017 в 01:02

Вы можете использовать эту функцию, которая получает promFactories List:

function executeSequentially(promiseFactories) {
    var result = Promise.resolve();
    promiseFactories.forEach(function (promiseFactory) {
        result = result.then(promiseFactory);
    });
    return result;
}

Promise Factory - это просто простая функция, которая возвращает Promise:

function myPromiseFactory() {
    return somethingThatCreatesAPromise();
}

Это работает, потому что обещание фабрики не создает обещание, пока его не попросят. Он работает так же, как и функция then - на самом деле, это одно и то же!

Вы вообще не хотите работать над множеством обещаний. По спецификации Promise, как только создается обещание, оно начинает выполняться. Итак, что вы действительно хотите, это массив обещающих фабрик ...

Если вы хотите узнать больше о Promises, вы должны проверить эту ссылку: https://pouchdb.com/2015/05/18/ мы-есть-а-проблема-с-promises.html

2
ответ дан 15 August 2018 в 15:37

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

Пока вы добавляете «возврат» перед вызовом каждого функцию, описывающую Promise и использующую этот пример в качестве основы, следующий вызов функции .then () СОСТОЯТСЯ после завершения предыдущего:

getRidOfOlderShoutsPromise = () => {
    return readShoutsPromise('BEFORE')
    .then(() => {
        return deleteOlderShoutsPromise();
    })
    .then(() => {
        return readShoutsPromise('AFTER')
    })
    .catch(err => console.log(err.message));
}

deleteOlderShoutsPromise = () => {
    return new Promise ( (resolve, reject) => {
        console.log("in deleteOlderShouts");
        let d = new Date();
        let TwoMinuteAgo = d - 1000 * 90 ;
        All_Shouts.deleteMany({ dateTime: {$lt: TwoMinuteAgo}}, function(err) {
            if (err) reject();
            console.log("DELETED OLDs at "+d);
            resolve();        
        });
    });
}

readShoutsPromise = (tex) => {
    return new Promise( (resolve, reject) => {
        console.log("in readShoutsPromise -"+tex);
        All_Shouts
        .find({})
        .sort([['dateTime', 'ascending']])
        .exec(function (err, data){
            if (err) reject();
            let d = new Date();
            console.log("shouts "+tex+" delete PROMISE = "+data.length +"; date ="+d);
            resolve(data);
        });    
    });
}
0
ответ дан 15 August 2018 в 15:37

Если вы хотите, вы можете использовать сокращение, чтобы сделать последовательное обещание, например:

[2,3,4,5,6,7,8,9].reduce((promises, page) => {
    return promises.then((page) => {
        console.log(page);
        return Promise.resolve(page+1);
    });
  }, Promise.resolve(1));

он всегда будет работать последовательно.

1
ответ дан 15 August 2018 в 15:37

Это касается того, как обрабатывать последовательность обещаний более общим образом, поддерживая динамические / бесконечные последовательности, основанные на реализации spex.sequence:

var $q = require("q");
var spex = require('spex')($q);

var files = []; // any dynamic source of files;

var readFile = function (file) {
    // returns a promise;
};

function source(index) {
    if (index < files.length) {
        return readFile(files[index]);
    }
}

function dest(index, data) {
    // data = resolved data from readFile;
}

spex.sequence(source, dest)
    .then(function (data) {
        // finished the sequence;
    })
    .catch(function (error) {
        // error;
    });

Не только это решение будет работать с последовательностями любого размера, но вы можете легко добавить к нему spex.sequence .

0
ответ дан 15 August 2018 в 15:37

Вот как я предпочитаю запускать задачи последовательно.

function runSerial() {
    var that = this;
    // task1 is a function that returns a promise (and immediately starts executing)
    // task2 is a function that returns a promise (and immediately starts executing)
    return Promise.resolve()
        .then(function() {
            return that.task1();
        })
        .then(function() {
            return that.task2();
        })
        .then(function() {
            console.log(" ---- done ----");
        });
}
Как насчет случаев с большим количеством задач? Например, 10?
function runSerial(tasks) {
  var result = Promise.resolve();
  tasks.forEach(task => {
    result = result.then(() => task());
  });
  return result;
}
52
ответ дан 15 August 2018 в 15:37
  • 1
    А как насчет случаев, когда вы не знаете точное количество задач? – damd 14 February 2016 в 17:51
  • 2
    А как насчет того, когда вы знаете количество задач, но только во время выполнения? – joeytwiddle 8 April 2016 в 11:26
  • 3
    Как насчет этого решения? [D0] pastebin.com/XJLaPtD9 – Kevin 10 April 2016 в 11:58
  • 4
    «вы не хотите работать над множеством обещаний. По обещанию spec, как только создается обещание, оно начинает выполняться. Так что вы действительно хотите, это массив обещающих фабрик & quot; см. Продвинутую ошибку №3 здесь: pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html – edelans 21 November 2016 в 15:22
  • 5
    Если вы хотите уменьшить линейный шум, вы также можете записать result = result.then(task); – Daniel Buckmaster 28 November 2016 в 02:18

Другие вопросы по тегам:

Похожие вопросы: