Как выполнить синхронизацию обещаний? [Дубликат]

Для меня, для Chrome / Webkit, document.width / jQuery(document).width() не работает. Когда я сделал мое окно маленьким и увеличил масштаб на моем сайте, чтобы появились горизонтальные полосы прокрутки, document.width / jQuery(document).width() не равнялся 1 при масштабировании по умолчанию. Это связано с тем, что document.width включает часть документа за пределами области просмотра.

Работала с window.innerWidth и window.outerWidth. По какой-либо причине в Chrome внешняя ширина измеряется в пикселях экрана, а внутренняя ширина измеряется в пикселях css.

var screenCssPixelRatio = (window.outerWidth - 8) / window.innerWidth;
if (screenCssPixelRatio >= .46 && screenCssPixelRatio <= .54) {
  zoomLevel = "-4";
} else if (screenCssPixelRatio <= .64) {
  zoomLevel = "-3";
} else if (screenCssPixelRatio <= .76) {
  zoomLevel = "-2";
} else if (screenCssPixelRatio <= .92) {
  zoomLevel = "-1";
} else if (screenCssPixelRatio <= 1.10) {
  zoomLevel = "0";
} else if (screenCssPixelRatio <= 1.32) {
  zoomLevel = "1";
} else if (screenCssPixelRatio <= 1.58) {
  zoomLevel = "2";
} else if (screenCssPixelRatio <= 1.90) {
  zoomLevel = "3";
} else if (screenCssPixelRatio <= 2.28) {
  zoomLevel = "4";
} else if (screenCssPixelRatio <= 2.70) {
  zoomLevel = "5";
} else {
  zoomLevel = "unknown";
}
170
задан Benjamin Gruenbaum 25 December 2014 в 12:24
поделиться

21 ответ

Обновление 2017: я бы использовал функцию async, если среда поддерживает ее:

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 = Q(); // Promise.resolve() without Q

  files.forEach(function(file){
      p = p.then(function(){ return readFile(file); }); // or .bind
  });
  return p;
};

Или более компактно, с уменьшением:

var readFiles = function(files) {
  return files.reduce(function(p, file) {
             return p.then(function(){ return readFile(file); });
         },Q()); // 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.
});

В Q у вас есть так же хорошо, как вы можете получить - вы можете немного сократить его с помощью Array.prototype.reduce и извлечь его в общий метод.

Если вы можете использовать Q.async (это вы на узле) все становится лучше:

Q.spawn(function* () {
    var results = [];
    for(var i = 0;i < files.length; i++){
        results.push(yield readFile(files[i]));
    }
    console.log(results);
});

Просто не забудьте запустить узел с --harmony и запомнить его экспериментальный atm.

187
ответ дан Benjamin Gruenbaum 22 August 2018 в 11:09
поделиться

Используйте 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
ответ дан Alexander Mills 22 August 2018 в 11:09
поделиться

Мой ответ основан на 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
ответ дан Community 22 August 2018 в 11:09
поделиться

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

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

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
ответ дан Hai Phan 22 August 2018 в 11:09
поделиться

Ваш подход не плох, но у него есть две проблемы: он проглатывает ошибки, и в нем используется 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
ответ дан JLRishe 22 August 2018 в 11:09
поделиться

Этот вопрос старый, но мы живем в мире 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
ответ дан joelnet 22 August 2018 в 11:09
поделиться
  • 1
    очень хорошо, спасибо, Array.prototype.concat.bind(result) является той частью, которую я отсутствовал, приходилось делать толкание к результатам вручную, которое работало, но было менее прохладно – zavr 3 February 2017 в 11:32
  • 2
    Поскольку мы все о современных JS, я считаю, что утверждение console.log.bind(console) в вашем последнем примере теперь обычно не нужно. В эти дни вы можете просто пройти console.log. Например. serial(funcs).then(console.log). Протестировано на текущих узлах и Chrome. – Molomby 1 August 2017 в 04:41
  • 3
    Это было немного сложно обернуть вокруг меня, но сокращение по существу делает это правильно? Promise.resolve([]).then((x) => { const data = mockApi('/data/1'); return Promise.resolve(x.concat(data)) }).then((x) => { const data = mockApi('/data/2'); return Promise.resolve(x.concat(data)); }); – danecando 30 May 2018 в 06:59
  • 4
    @danecando, да, это выглядит правильно. Вы также можете отказаться от Promise.resolve в возврате, любые возвращаемые значения будут автоматически разрешены, если вы не назовете Promise.reject на них. – joelnet 31 May 2018 в 23:47
  • 5
    – bufferoverflow76 30 August 2018 в 14:40

Я использую следующий код для расширения объекта 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
ответ дан josemontesp 22 August 2018 в 11:09
поделиться

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

4
ответ дан Mikael Lepistö 22 August 2018 в 11:09
поделиться
  • 1
    Еще лучше: Promise.each(filtes, fs.readFileAsync). Кстати, вам не нужно делать .bind(fs)? – Bergi 6 May 2015 в 17:16
  • 2
    Кажется, никто здесь не понимает разницу между массивом и последовательностью, что последний подразумевает неограниченный / динамический размер. – vitaly-t 16 October 2015 в 17: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)
  • Использует функции стрелок;

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

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
ответ дан Molomby 22 August 2018 в 11:09
поделиться

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

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

UPDATE

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

21
ответ дан Pooya 22 August 2018 в 11:09
поделиться

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

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

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

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

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

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

  • работает с любыми обеими версиями jQuery lib - даже предсовместимыми версиями - используется только .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. С помощью A + libs будут усваиваться иностранные обещания.

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

И как только вы впитаете это, возможно, слегка раздумывая, когда вы понимаете, что этап .map() на самом деле не нужен! Вся работа, параллельные вызовы плюс последовательная обработка в правильном порядке, может быть достигнута только с помощью 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.
    });
};

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

0
ответ дан Roamer-1888 22 August 2018 в 11:09
поделиться
  • 1
    Я не думаю, что это хорошая идея ответить на вопросы, противоречащие намерениям OPs ... – Bergi 9 September 2015 в 13:07
  • 2
    Эта вещь sequence.then(() => filePromise) является антипаттерном - она ​​не распространяет ошибки, как только они могут (и создает unhandledRejection в libs, которые их поддерживают). Вы скорее должны использовать Q.all([sequence, filePromise]) или $.when(sequence, filePromise). По общему признанию, это поведение может быть тем, что вы хотите, когда пытаетесь игнорировать или пропускать ошибки, но вы должны хотя бы упомянуть об этом как о недостатке. – Bergi 9 September 2015 в 13:10
  • 3
    @Bergi, я надеюсь, что OP войдет и будет судить о том, действительно ли это противоречит его намерениям или нет. Если нет, я удалю ответ, я думаю, пока я надеюсь, что оправдал свою позицию. Спасибо, что приняли это достаточно серьезно, чтобы обеспечить достойную обратную связь. Можете ли вы подробнее объяснить анти-шаблон или предоставить ссылку, пожалуйста? То же самое относится к статье, в которой я нашел базовый шаблон ? – Roamer-1888 9 September 2015 в 16:47
  • 4
    Да, третья версия его кода (то есть «как параллельная, так и последовательная») имеет ту же проблему. "Антипаттерн" требует сложной обработки ошибок и подвержен асинхронному подключению обработчиков, что вызывает события unhandledRejection. В Bluebird вы можете обойти это, используя sequence.return(filePromise), который имеет такое же поведение, но отлично обрабатывает отклонения. Я не знаю никаких ссылок, я только что придумал это - я не думаю, что шаблон «(анти)» имеет имя еще. – Bergi 9 September 2015 в 17:43
  • 5
    @Bergi, вы можете ясно видеть то, что я не могу :( Интересно, нужно ли где-то документировать этот новый анти-шаблон? – Roamer-1888 9 September 2015 в 20: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
ответ дан Salketer 22 August 2018 в 11:09
поделиться
  • 1
    Невероятное решение, это было лучшее из того, что я нашел почти за неделю, когда он пробовал ... Это очень хорошо объяснено, имеет логические внутренние имена, хороший пример (может быть, лучше), я могу называть его безопасным, как многие по мере необходимости, и включает в себя возможность установки обратных вызовов. просто NICE! (Просто изменил название на то, что делает меня более понятным) .... РЕКОМЕНДАЦИЯ для других ... вы можете итерации объекта с помощью «Object.keys ( myObject )» в качестве вашего объекта objects_array, – DavidTaubmann 23 February 2017 в 09:27
  • 2
    Спасибо за ваш комментарий! Я тоже не использую это имя, но я хотел сделать это более очевидным / простым здесь. – Salketer 23 February 2017 в 09: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
ответ дан Shridhar Gupta 22 August 2018 в 11:09
поделиться
  • 1
    Он использует ES2015 Promises вместо библиотеки типа Q или bluebird, которые имеют некоторые специальные функции. – Shridhar Gupta 22 April 2016 в 18:37
  • 2
    Кажется, он использует подчеркивание. Вы можете упростить files.forEach, если файлы представляют собой массив. – Gustavo Rodrigues 25 July 2016 в 18:36
  • 3
    Ну ... это ES5. Путь ES6 будет for (file of files) {...}. – Gustavo Rodrigues 26 July 2016 в 20:40
  • 4
    Вы говорите, что вы не должны использовать Promise.resolve() для создания обещанного обещания в реальной жизни. Почему нет? Promise.resolve() кажется более чистым, чем new Promise(success => success()). – canac 9 January 2017 в 22:28
  • 5
    @canac Извините, это была просто шутка с игрой на словах («пустые обещания ..»). Определенно используйте Promise.resolve(); в вашем коде. – Shridhar Gupta 10 January 2017 в 22: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/we-have-a-problem-with-promises.html

2
ответ дан sidanmor 22 August 2018 в 11:09
поделиться

Если кому-то нужен гарантированный способ СТРОГО последовательного способа разрешения обещаний при выполнении операций 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
ответ дан Ula 22 August 2018 в 11:09
поделиться

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

[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
ответ дан Victor Palomo 22 August 2018 в 11:09
поделиться

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

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

0
ответ дан vitaly-t 22 August 2018 в 11:09
поделиться

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

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
ответ дан vp_arth 22 August 2018 в 11:09
поделиться
  • 1
    А как насчет случаев, когда вы не знаете точное количество задач? – damd 14 February 2016 в 14:51
  • 2
    А как насчет того, когда вы знаете количество задач, но только во время выполнения? – joeytwiddle 8 April 2016 в 08:26
  • 3
    Как насчет этого решения? pastebin.com/XJLaPtD9 – Kevin 10 April 2016 в 08:58
  • 4
    «вы не хотите работать над множеством обещаний. По обещанию spec, как только создается обещание, оно начинает выполняться. Так что вы действительно хотите, это массив обещающих фабрик & quot; см. Продвинутую ошибку №3 здесь: pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html – edelans 21 November 2016 в 12:22
  • 5
    Если вы хотите уменьшить линейный шум, вы также можете записать result = result.then(task); – Daniel Buckmaster 27 November 2016 в 23:18
  • 6
0
ответ дан Gil Epshtain 5 November 2018 в 08:42
поделиться
Другие вопросы по тегам:

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