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

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

var readFiles = function(files) {
  return new Promise((resolve, reject) => 

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

   readSequential(0); // Start!

  });
};

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

в chrome и Promise.all, но это привело к одновременному выполнению всех вызовов readFile, чего я и хочу не :

var readFiles = function(files) {
  return Promise.all(files.map(function(file) {
    return readFile(file);
  }));
};

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

async function readFiles(files) {
  for(const file of files) {
    await readFile(file);
  }
};

Если вы хотите, вы можете отложить чтение файлов до тех пор, пока они вам не понадобятся, с помощью асинхронного генератора (если ваша среда это поддерживает):

async function* readFiles(files) {
  for(const file of files) {
    yield await readFile(file);
  }
};

Обновление: если подумать — я мог бы вместо этого использовать цикл for:

var readFiles = function(files) {
  var p = Promise.resolve(); // Q() in q

  files.forEach(file =>
      p = p.then(() => readFile(file)); 
  );
  return p;
};

Или более компактно с помощью Redu:

var readFiles = function(files) {
  return files.reduce((p, file) => {
     return p.then(() => readFile(file));
  }, Promise.resolve()); // initial
};

В других библиотеках обещаний (например, когда и Bluebird) у вас есть полезные методы для этого.

Например, Bluebird будет:

var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs"));

var readAll = Promise.resolve(files).map(fs.readFileAsync,{concurrency: 1 });
// if the order matters, you can use Promise.each instead and omit concurrency param

readAll.then(function(allFileContents){
    // do stuff to read files.
});

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

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

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;
}

Этот вопрос старый, но мы живем в мире 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))

Чтобы сделать это просто в 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;
  }

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

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

UPDATE

items-обещание — готовый к использованию NPM пакет делает то же самое.

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

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 );

Надеюсь, это сэкономит кому-то время …

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

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

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

ECMAScript 6

. Это то, что позволяет нам применять такие понятия, как суперкласс или подкласс в JavaScript. [t1, t2, t3] Обычно все начинается с чего-то рядом с этим (я удалил все лишние вещи, не важно): Promise.resolve().then(t1).then(t2).then(t3). Это поведение уменьшить.

Насколько мне известно,

Первые Вам необходимо составить список задач! Задача — это функция, которая не принимает аргументов Если вам нужно передать аргументы в вашу функцию, используйте bind или другие методы для создания задачи. Например:

var tasks = files.map(file => processFile.bind(null, file))
inSequence(tasks).then(...)

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

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

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

  • Применяет функцию к элементам в серии
  • Преобразовывается в массив результатов
  • SMTPJs
  • Использует функции стрелок ; красиво и лаконично

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

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).

Использование 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 inSeries = function(providers){

  const seed = Promise.resolve(null); 

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

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

Фактический вопрос при работе с Reduce защитите пустые массивы или массивы только с одним элементом, так что этот метод является вашим Лучшая ставка:

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

    const inSeries = function(providers, initialVal){

        if(providers.length < 1){
            return Promise.resolve(null)
        }

        return providers.reduce((a,b) => a.then(b), providers.shift()(initialVal));
    };

, а затем вызвать его следующим образом:

inSeries(providers, 1).then(v => {
   console.log(v);  // 7
});

, который я создал этот простой метод на Обещание объекта:

Создать и добавить метод 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.

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

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, как только обещание создано, оно начинает выполняться. Так что вам действительно нужен массив фабрик обещаний …

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

Мой ответ основан на https://stackoverflow.com/a/31070150/7542429 Как заметил Берги, я думаю, что лучшее и ясное решение — использовать BlueBird.each, код ниже: Глядя на вывод

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
});

Мне очень понравился ответ @ 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)
  })

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

const BlueBird = require('bluebird');
BlueBird.each(files, fs.readFileAsync);

Код

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

/*
    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);

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

[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));

Я не понимаю, почему люди предлагают такие сложные решения. Вот более простая логика

С Async / Await — лучшее решение, если у вас есть поддержка ES7

Без Async / Await

function downloadFile(fileUrl) { ... } // This function return a Promise

async function main()
{
  var filesList = [...];
  for (var i = 0; i <= filesList.length; i  )
  {
    await downloadFile(filesList[i]);
  }
}

На основе заголовка вопроса: «Разрешить обещания одно за другим (то есть в sequence)? «, мы могли бы понять, что OP больше заинтересован в последовательной обработке обещаний по расчету, чем в последовательных вызовах

function downloadFile(fileUrl) { ... } // This function return a Promise

function downloadRecursion(filesList, index)
{
  if (index < filesList.length)
  {
    downloadFile(filesList[index]).then(function()
    {
      index  ;
      downloadRecursion(filesList, index); // self invocation - recursion!
    });
  }
  else
  {
    return Promise.resolve();
  }
}

function main()
{
  var filesList = [...];
  downloadFile(filesList, 0);
}

per se Этот ответ предлагается: Глядя на вывод

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

  • , чтобы представить жизнеспособные альтернативные шаблоны посетителям этой страницы — включая ОП, если он все еще заинтересован в течение года.
  • Если одновременные вызовы действительно нежелательны, см. Ответ Бенджамина Грюнбаума, в котором подробно рассматриваются последовательные вызовы (и т. Д.).
  • Да, это так :) Я думаю, что вы также можете сделать

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

Заманчиво подумать, что вы должны использовать

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

  • работает с любой обещанной библиотекой — даже с пре-совместимыми версиями jQuery — только .then() используется.
  • дает возможность пропустить ошибку или остановку при ошибке, в зависимости от того, что вам нужно хочу с модом в одну строку.

Здесь написано для 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.
    });
};

Примечание: только один фрагмент Q() относится к Q. Для jQuery вы должны убедиться, что readFile () возвращает обещание jQuery. С помощью libs иностранные обещания будут ассимилированы.

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

И после того, как вы это освоите, может быть, это немного ошеломляет, когда вы понимаете, что .map() Доступ к междоменным элементам iframe в React с помощью (srcDoc): — reduce(), плюс дополнительное преимущество дополнительной гибкости для:

  • преобразует параллельные асинхронные вызовы в последовательные асинхронные вызовы, просто перемещая одну строку — потенциально полезно во время разработки.

Вот и снова 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.
    });
};

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

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

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

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!
};

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

Когда у вас включен html5Mode,

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);
        });    
    });
}

Метод последовательности push и pop можно использовать для последовательности обещаний. Вы также можете выдвигать новые обещания, когда вам нужны дополнительные данные. Этот код я буду использовать в загрузчике React Infinite для загрузки последовательности страниц.

var promises = [Promise.resolve()];

function methodThatReturnsAPromise(page) {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			console.log(`Resolve-${page}! ${new Date()} `);
			resolve();
		}, 1000);
	});
}

function pushPromise(page) {
	promises.push(promises.pop().then(function () {
		return methodThatReturnsAPromise(page)
	}));
}

pushPromise(1);
pushPromise(2);
pushPromise(3);

Это расширение того, как обрабатывать последовательность обещаний более общим способом, поддерживая динамические / бесконечные последовательности на основе реализации 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;
    });

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