#javascript #es6-promise
Вопрос:
Следующая функция выполняется после операции перетаскивания нескольких файлов.
function getFilesInfo(ev){
for (let i = 0; i < ev.dataTransfer.items.length; i ) {
if (ev.dataTransfer.items[i].kind === 'file') {
let file = ev.dataTransfer.items[i].getAsFile();
//getFileInfo adds string to DOM element
//Note the promise usage ...
file.arrayBuffer().then((data)=>getFileInfo(file.name,data));
}
}
}
Я не могу понять, как вызвать функцию после завершения всех обещаний в этой функции.
В принципе, я хочу что-то вроде этого, последовательно:
getFilesInfo(ev);
//getFileInfo(<file1>);
//getFileInfo(<file2>);
//getFileInfo(<file3>);
// etc.
//run only after all getFileInfo() calls have finished
processResults();
Сложность заключается в том, что чтение файлов создает обещание для каждого файла, который вызывается, когда файл был считан в память (часть arrayBuffer()
вызова). Я не могу понять, как задержать processResults
, потому getFilesInfo
что завершается после того, как были запущены все вызовы чтения, а не (из того, что я могу сказать) после завершения getFileInfo
функций.
Кажется, что, возможно, я мог бы каким-то образом добавить все вызовы ArrayBuffer в массив, а затем выполнить некоторые обещания (может быть?), Но это кажется неудобным, и я даже не уверен, как бы я это сделал.
Комментарии:
1. Что
getFileInfo
делает, что он возвращает и почему неprocessResults
принимает аргументов?2. » Я мог бы каким — то образом добавить все вызовы ArrayBuffer в массив, а затем выполнить связывание обещаний » — да, используйте
Promise.all
. Это совсем не неловко!3. @Bergi
getFileInfo
обновляет строку в DOM,processResults
считывает эту глобальную переменную и что-то с ней делает.Promise.all
казалось уместным, но я не был уверен, как это реализовать с помощью цикла …4.Добавьте все обещания, созданные с помощью
file.arrayBuffer().then(…)
, в массив, а затем вызовитеPromise.all
после цикла. И создайтеgetFileInfo
return
значение, не используйте глобальную переменную для передачи результатовprocessResults
.
Ответ №1:
Вы можете использовать Promise.all
, чтобы дождаться завершения массива обещаний:
async function getFilesInfo(ev) {
// create list of jobs
const jobs = [];
for (const item of ev.dataTransfer.items) {
if (item.kind === 'file') {
let file = item.getAsFile();
jobs.push(file.arrayBuffer().then(data => {
getFileInfo(file.name, data);
}));
}
}
// wait for all promise to fullfil
await Promise.all(jobs);
}
https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Ответ №2:
Вы могли бы сделать это таким образом:
function getFilesInfo(ev){
return ev.dataTransfer.items.filter(item=>item.kind === 'file').map(item=>{
let file = item.getAsFile();
return file.arrayBuffer().then((data)=>getFileInfo(file.name,data));
});
}
Promise.all(...getFilesInfo(ev)).then(_=>{
processResults();
});
// or with async/await
(async ()=>{
await Promise.all(...getFilesInfo(ev));
processResults();
})()
Ответ №3:
async function getFilesInfo(ev) {
await Promise.all(ev.dataTransfer.items.map(async (i) => {
const file = i.getAsFile();
const data = await file.arrayBuffer();
return getFileInfo(file.name, data);
}));
}
await getFilesInfo(ev); // will be awaited until all the promises are resolved
processResults();
Дайте мне знать, если это поможет.
Ответ №4:
Концептуальное препятствие, с которым я столкнулся, заключалось в том, что я думал о then
функции как о возврате результатов, а не обещаний. Кроме того, многие примеры, которые я видел, Promise.all
обычно представляют собой просто объединение явных вызовов, а не построение массива в цикле.
Как предложил Берги, я просто добавил вызовы в массив, а затем передал этот массив в Promise.all
function getFilesInfo(ev) {
// create list of jobs
let jobs = [];
for (const item of ev.dataTransfer.items) {
if (item.kind === 'file') {
let file = item.getAsFile();
jobs.push(file.arrayBuffer().then(data => {
getFileInfo(file.name, data);
}));
}
}
return jobs;
}
//The call in the parent
let jobs = getFilesInfo(ev);
Promise.all(jobs).then(processResults);