编辑
所以我决定进一步研究这种情况。我试图链接各种函数返回的承诺,但到目前为止我没有运气。
我的应用程序的这一部分旨在不完全异步地工作,这就是现在引起巨大头痛的原因。
首先:Imagepicker 本身返回一个承诺。在这个承诺中,我拥有用户从图库中选择的所有图片 - 我已将限制设置为 5,但这无关紧要。
this.imagePicker.getPictures(this.options)
// Prima promise, ottengo le foto scelte dalla gallery
.then((res) => {
var count = 1;
for (var i = 0; i < res.length; i++) {
var total = res.length;
let path:string = res[i].toString();
// Estraggo il nome e il percorso del file
var currentName = path.substr(path.lastIndexOf('/') + 1);
var correctPath = path.substr(0, path.lastIndexOf('/') + 1);
})
现在,如果我正在使用除 typescript/javascript 之外的任何其他语言,我会在 for 循环内调用上传方法,每个文件一个,获取该特定文件的结果并调用将完成的第二个上传方法(元数据)上传并移动到 for 循环中的下一个文件。
但我不会使用任何其他语言,所以最初我想:“嘿,让我们将承诺嵌套起来!”。哪个有效,几乎完美无缺,直到您开始遇到一些网络问题,然后砰的一声,您永远不知道哪些文件被上传,哪些文件直到最后才上传。
问题是我如何链接来自不同功能的承诺?
我是这样试的(上面的代码现在如下图)
this.imagePicker.getPictures(this.options)
// Prima promise, ottengo le foto scelte dalla gallery
.then((res) => {
var count = 1;
for (var i = 0; i < res.length; i++) {
var total = res.length;
let path:string = res[i].toString();
// Estraggo il nome e il percorso del file
var currentName = path.substr(path.lastIndexOf('/') + 1);
var correctPath = path.substr(0, path.lastIndexOf('/') + 1);
this.file.readAsArrayBuffer(correctPath.toString(), currentName)
.then( result => {
let blob = new Blob([result], {type: "image/jpeg"});
return blob;
})
}
})
但是话又说回来,如果我想使用那个“blob”,我必须在那个承诺中链接一个 .then ,所以无论如何我最终都会嵌套它们。更糟糕的是,我现在知道一些代码将在承诺返回结果之前执行。而这正是发生的事情。循环继续进行,因为在调用 this.file.readAsArrayBuffer 之后,代码会跳转并关闭 for 循环迭代,同时等待 promise 实现,同时循环的第二次迭代会触发一个新的 promise。
上面经过编辑的代码片段和下面的原始代码都无法正确显示所显示的加载控制器,但它在真正开始下载之前就消失了,或者,如果我删除持续时间选项并放置一个 this.loadingController。在上传承诺的.then 部分中使用dismiss(),最后一个loadingController 永远保持开启状态,它永远不会被解除。
这是原始代码(在找到可行的解决方案之前,我仍然使用它)。
要求回顾: - 用户从图库中最多选择 5 张图片 - 5 张图片中的每一张都使用合适的加载控制器模式一次上传一次,直到上传结束,然后相应的元数据也被上传。- 在下载结束时会显示 Toast 向用户提供反馈 - 从下一个文件重新开始
在这种特殊情况下,我对用户在上传时可以做其他事情不感兴趣:我希望他们“卡住”加载直到结束。
原代码如下:
照片选择器 ()
photoPicker() {
if (!this.uploadForm.invalid) {
this.options = {
width: 4000,
quality: 100,
outputType: 0,
}
this.imagePicker.getPictures(this.options)
.then((res) => {
var count = 1;
for (var i = 0; i < res.length; i++) {
var total = res.length;
let path:string = res[i].toString();
// Estraggo il nome e il percorso del file
var currentName = path.substr(path.lastIndexOf('/') + 1);
var correctPath = path.substr(0, path.lastIndexOf('/') + 1);
if ( CONFIG.DEV == 1) {
let datetime = new Date();
console.log('[objects-docs-multiupload] @ ' + datetime.toISOString() + ' picture path: ');
console.log(path);
}
// Leggo il contenuto in un buffer
this.file.readAsArrayBuffer(correctPath.toString(), currentName)
.then( result => {
if ( CONFIG.DEV == 1) {
let datetime = new Date();
console.log('[objects-docs-multiupload] @ ' + datetime.toISOString() + 'data result: ');
console.log(result);
}
// E uso il contenuto per creare un blob con il binario del file
let blob = new Blob([result], {type: "image/jpeg"});
if ( CONFIG.DEV == 1) {
let datetime = new Date();
console.log('[objects-docs-multiupload] @ ' + datetime.toISOString() + 'data blob: ');
console.log(blob);
}
// invoco il metodo per caricare il file
this.uploadFile(blob, currentName, count, total);
count = count + 1;
});
}
}, (err) => {
alert(err);
});
} else {
this.showToastAlert('Compilare i dati del documento', 'error');
return;
}
if (this.uploadForm.controls.recipient.value != '') {
this.createTask();
}
this.router.navigateByUrl('/objects-dashboard/'+this.obj_id);
}
上传文件()
async uploadFile(file, fileName, counter, total) {
// Chiamo la funzione asincrona per la mascherina di caricamento
// in modo da dare visibilità al fatto che la app è ferma per fare un upload
this.presentUpLoading(counter, total);
// Contatto il metodo uploadFile del rest
this.restProvider.uploadFile(file, fileName)
.then(data => {
if ( CONFIG.DEV == 1) {
let datetime = new Date();
console.log('[objects-dashboard] @ ' + datetime.toISOString() + 'response from uploadFile: ');
console.log(data);
}
// Qui devo fare un piccolo trucco
// devo ritrasformare in json e quindi rifare il parse
// per otternere un oggetto (recryptData) da usare per assemblare
// l'url cui fare il redirect
let decryptedData = JSON.stringify(data);
let recryptData = JSON.parse(decryptedData);
this.loadDoc(recryptData.uuid, recryptData.uploadName, counter, total);
});
}
加载文档()
async loadDoc(uuid, uploadName, counter, total) {
let upload = {
name: this.uploadForm.controls.name.value+'_'+counter,
categoryId: this.uploadForm.controls.cat_id.value.substring(3),
description: this.uploadForm.controls.description.value+'_'+counter,
filename: uploadName,
uuid: uuid,
objectId: this.obj_id,
};
if(CONFIG.DEV ==1) { console.log(upload); };
this.restProvider.uploadDoc(upload)
.then( data => {
if (data['Result'] == 'Success') {
// OK: Richiamo la funzione showToastAlert
// per mostrare l'avviso Toast
this.showToastAlert('Documento Caricato '+counter+' di '+total, 'success');
// Rimando alla pagina messages-dashboard
//this.router.navigateByUrl('/objects-dashboard/'+this.obj_id);
} else {
// KO: Richiamo la funzione showToastAlert
// per mostrare l'avviso Toast
this.showToastAlert('Documento NON caricato '+counter+' di '+total, 'error');
}
});
}
我终于设法让整个事情正常工作。事实证明,我以错误的方式调用了这些函数。我以正确的方式使用 await/async 解决了这个问题。
for..loop 的内部:
this.imagePicker.getPictures(this.options)
// Prima promise, ottengo le foto scelte dalla gallery
.then(async (res) => {
var count = 1;
for (var i = 0; i < res.length; i++) {
var total = res.length;
let path:string = res[i].toString();
// Estraggo il nome e il percorso del file
var currentName = path.substr(path.lastIndexOf('/') + 1);
var correctPath = path.substr(0, path.lastIndexOf('/') + 1);
// Leggo il contenuto in un buffer
await this.file.readAsArrayBuffer(correctPath.toString(), currentName)
.then( (result) => {
// E uso il contenuto per creare un blob con il binario del file
this.blob = new Blob([result], {type: "image/jpeg"});
return this.blob;
})
// Visualizzo il loading controller che indica il caricamento in corso
if ( CONFIG.DEV == 1 ) console.log("Presenting loading controller for counter "+count+" of "+total);
this.presentUpLoading(count, total);
// Inizio l'upload del file col metodo rest e aspetto l'esito
if ( CONFIG.DEV == 1 ) console.log("Starting upload of "+currentName);
await this.uploadFile(this.blob, currentName,count, total);
if ( CONFIG.DEV == 1 ) console.log("RecryptData: ");
if ( CONFIG.DEV == 1 ) console.log(this.recryptData);
if ( CONFIG.DEV == 1 ) console.log("Loading Doc Metadata");
// Carico i metadati dei files e aspetto l'esito
await this.loadDoc(this.recryptData.uuid, this.recryptData.uploadName, count, total);
if ( CONFIG.DEV == 1 ) console.log("Dismissing loading controller");
// Elimino il loading controller e passo al successivo upload
this.dismissUpLoading();
if ( CONFIG.DEV == 1 ) console.log("incrementing counter");
count++;
}
})
这个技巧是让 getPictures 的 promise 结果成为一个异步函数,这样我就可以在我必须在循环中运行的所有其他 promise 中使用 await。通过这种方式,所有的承诺都在下一次执行循环之前得到履行,而且我还能够修复 loadingController,它现在在整个上传时间都保持打开状态。
为了让实际的上传函数与 await 一起工作,我必须将该函数的结果转换为 Promise 本身:
uploadFile(file, fileName, counter, total) {
// Chiamo la funzione asincrona per la mascherina di caricamento
// in modo da dare visibilità al fatto che la app è ferma per fare un upload
return new Promise((resolve) => {
// Contatto il metodo uploadFile del rest
this.restProvider.uploadFile(file, fileName)
.then((data) => {
// Qui devo fare un piccolo trucco
// devo ritrasformare in json e quindi rifare il parse
// per otternere un oggetto (recryptData) da usare per assemblare
// l'url cui fare il redirect
let decryptedData = JSON.stringify(data);
this.recryptData = JSON.parse(decryptedData);
//this.loadDoc(this.recryptData.uuid, this.recryptData.uploadName, counter, total);
resolve(this.recryptData);
});
});
}
多亏了这一点,数组“recryptData”可用于下一个函数(loadDoc),并且我能够避免承诺嵌套。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句