Для меня, для 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";
}
Обновление 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.
Используйте 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);
};
приятно и легко ... вы должны иметь возможность повторно использовать то же самое семена для производительности и т. д.
Мой ответ основан на 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
});
Я создал этот простой метод для объекта 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.
Promise.all
и ваш Promise.sequence
. Один делает итерабельность обещаний, другой принимает множество функций, которые возвращают обещания.
– Bergi
29 July 2015 в 14:14
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)
})
Это небольшое изменение другого ответа выше. Использование собственных обещаний:
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(...)
Ваш подход не плох, но у него есть две проблемы: он проглатывает ошибки, и в нем используется 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!
};
Этот вопрос старый, но мы живем в мире 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))
Array.prototype.concat.bind(result)
является той частью, которую я отсутствовал, приходилось делать толкание к результатам вручную, которое работало, но было менее прохладно
– zavr
3 February 2017 в 11:32
console.log.bind(console)
в вашем последнем примере теперь обычно не нужно. В эти дни вы можете просто пройти console.log
. Например. serial(funcs).then(console.log)
. Протестировано на текущих узлах и Chrome.
– Molomby
1 August 2017 в 04:41
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
Я использую следующий код для расширения объекта 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);
Самое лучшее решение, которое я смог выяснить, было с обещаниями bluebird
. Вы можете просто сделать Promise.resolve(files).each(fs.readFileAsync);
, который гарантирует, что обещания разрешаются последовательно в порядке.
Promise.each(filtes, fs.readFileAsync)
. Кстати, вам не нужно делать .bind(fs)
?
– Bergi
6 May 2015 в 17:16
Мое предпочтительное решение:
function processArray(arr, fn) {
return arr.reduce(
(p, v) => p.then((a) => fn(v).then(r => a.concat([r]))),
Promise.resolve([])
);
}
Это не принципиально отличается от других, опубликованных здесь, но:
Пример использования:
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).
Простая утилита для стандартного обещания Node.js:
function sequence(tasks, fn) {
return tasks.reduce((promise, task) => promise.then(() => fn(task)), Promise.resolve());
}
UPDATE
items-prom - это готовый к использованию пакет NPM, который делает то же самое .
Исходя из названия вопроса: «Решение обещает одно за другим (то есть в последовательности)?», мы могли бы понять, что ОП больше интересуется последовательной обработкой обещаний по расчету, чем последовательными вызовами per se .
Этот ответ предлагается:
Если одновременные вызовы действительно не нужны, см. ответ Бенджамина Грюнбаума, который охватывает последовательные вызовы (и т. д.) всесторонне.
Если, однако, вы заинтересованы (для повышения производительности) в шаблонах, которые допускают одновременные вызовы, за которыми следует последовательная обработка ответов, тогда, пожалуйста, прочитайте.
Заманчиво думать, что вам нужно использовать Promise.all(arr.map(fn)).then(fn)
(как я делал много раз), или саунд-мотив Promise lib (особенно Bluebird's), однако (с учетом этой статьи ) шаблон arr.map(fn).reduce(fn)
выполнит это задание с теми преимуществами, которые он имеет:
.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.
});
};
Это базовый шаблон. Если вы захотите также предоставить данные (например, файлы или некоторые их преобразования) вызывающему, вам понадобится мягкий вариант.
sequence.then(() => filePromise)
является антипаттерном - она не распространяет ошибки, как только они могут (и создает unhandledRejection
в libs, которые их поддерживают). Вы скорее должны использовать Q.all([sequence, filePromise])
или $.when(sequence, filePromise)
. По общему признанию, это поведение может i> быть тем, что вы хотите, когда пытаетесь игнорировать или пропускать ошибки, но вы должны хотя бы упомянуть об этом как о недостатке.
– Bergi
9 September 2015 в 13:10
unhandledRejection
. В Bluebird вы можете обойти это, используя sequence.return(filePromise)
, который имеет такое же поведение, но отлично обрабатывает отклонения. Я не знаю никаких ссылок, я только что придумал это - я не думаю, что шаблон «(анти)» имеет имя еще.
– Bergi
9 September 2015 в 17:43
Мне пришлось запускать много последовательных задач и использовать эти ответы для создания функции, которая будет заниматься обработкой любой последовательной задачи ...
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 );
Надеюсь, что это кому-то поможет ...
Для этого просто в 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;
}
files.forEach
, если файлы представляют собой массив.
– Gustavo Rodrigues
25 July 2016 в 18:36
for (file of files) {...}
.
– Gustavo Rodrigues
26 July 2016 в 20:40
Promise.resolve()
для создания обещанного обещания в реальной жизни. Почему нет? Promise.resolve()
кажется более чистым, чем new Promise(success => success())
.
– canac
9 January 2017 в 22:28
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
Если кому-то нужен гарантированный способ СТРОГО последовательного способа разрешения обещаний при выполнении операций 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);
});
});
}
Если вы хотите, вы можете использовать сокращение, чтобы выполнить последовательное обещание, например:
[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));
он всегда будет работать последовательно.
Это расширит процесс обработки последовательности обещаний более общим образом, поддерживая динамические / бесконечные последовательности, основанные на реализации 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;
});
Не только это решение будет работать с последовательностями любого размера, но вы можете легко добавить дросселирование данных и балансировку нагрузки к нему.
Вот как я предпочитаю запускать задачи последовательно.
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?
blockquote>function runSerial(tasks) { var result = Promise.resolve(); tasks.forEach(task => { result = result.then(() => task()); }); return result; }
result = result.then(task);
– Daniel Buckmaster
27 November 2016 в 23:18