链接多个承诺,包括 Promise.all
Chaining multiple promises, including Promise.all
我有一个要求需要链接承诺。在我的 Ionic 应用程序中,我需要遍历文件列表并将它们压缩。然后需要将 zip 存储在设备本身上(在本例中为 iPhone)。
我已经有了需要压缩成数组的文件列表。因此,我正在遍历它们并使用 $cordovaFile 获取这些文件的二进制内容。然后我将二进制文件添加到 JSZip 对象。最终结果应该是将所有文件的二进制内容添加到 zip.file,以便生成一个 zip 文件。
//wrapping in Promise.all so that we don't proceed until we have the content of all files added to zip
var zip = new JSZip();
return Promise.all(
filesList.forEach(function(file) {
console.log('file to be added using $cordovaFile '+file);
// Getting the content of each file using $cordovaFile. This returns a promise.
return $cordovaFile.readAsBinaryString(cordova.file.dataDirectory + $rootScope.username, file)
.then(function(binaryData) {
return new Promise(function(resolve, reject) {
//Adding content of all files to zip.file so that it can be zipped in the next step.
resolve(zip.file(file, binaryData, {binary: true}));
})
})
.catch(function(error) {
console.log('Error during fetch content or zipping '+JSON.stringify(error));
})
})
)
一旦 zip.file 拥有所有内容,我将调用 JSZip 中的另一个函数来生成 zip。这也是 return 一个承诺,所以我需要链接到 $cordovaFile.writeFile,以便可以在本地写入 zip。 $cordovaFile.writeFile 也是 return 一个 Promise,它将是链中的最后一个 Promise。
.then(function(zipData) {
// async request to generate the zip
return zipData.generateAsync({type:"blob"});
}).then(function (blob) {
// once we have the zip, save it to the device
$cordovaFile.writeFile(cordova.file.dataDirectory+$rootScope.username, 'abc.zip', blob, true)
.then(function(data) {
console.log('Zip file written to device at '+cordova.file.dataDirectory+$rootScope.username);
})
}).catch(function(error) {
console.log('Error while zipping and writing '+JSON.stringify(error));
})
这是完整代码的样子
var zipFiles = function(filesList) {
var zip = new JSZip();
return Promise.all(
filesList.forEach(function(file) {
return $cordovaFile.readAsBinaryString(cordova.file.dataDirectory + $rootScope.username, file)
.then(function(binaryData) {
return new Promise(function(resolve, reject) {
resolve(zip.file(file, binaryData, {binary: true}));
})
})
.catch(function(error) {
console.log('Error during fetch content or zipping '+JSON.stringify(error));
})
})
)
.then(function(zipData) {
return zipData.generateAsync({type:"blob"});
}).then(function (blob) {
$cordovaFile.writeFile(cordova.file.dataDirectory+$rootScope.username, 'abc.zip', blob, true)
.then(function(data) {
console.log('Zip file written to device at '+cordova.file.dataDirectory+$rootScope.username);
})
}).catch(function(error) {
console.log('Error while zipping and writing '+JSON.stringify(error));
})
}
挑战在于,在 Promise.all 完成后,什么也不会执行。所以,没有任何开始 'then(function(zipData)' 被执行。
我觉得这与我链接 Promises 的方式有关。任何帮助将不胜感激。
Promise.all 从未解析的原因是 filesList.forEach
从未 returns 任何值。
我认为修改为 fileList.map
可以解决您的问题。
所以按如下方式更改您的代码:
var zipFiles = function(filesList) {
var zip = new JSZip();
return Promise.all(
filesList.map(function(file) {
return $cordovaFile.readAsBinaryString(cordova.file.dataDirectory + $rootScope.username, file)
.then(function(binaryData) {
return new Promise(function(resolve, reject) {
resolve(zip.file(file, binaryData, {binary: true}));
})
})
.catch(function(error) {
console.log('Error during fetch content or zipping '+JSON.stringify(error));
})
})
)
.then(function(zipData) {
return zipData.generateAsync({type:"blob"});
}).then(function (blob) {
$cordovaFile.writeFile(cordova.file.dataDirectory+$rootScope.username, 'abc.zip', blob, true)
.then(function(data) {
console.log('Zip file written to device at '+cordova.file.dataDirectory+$rootScope.username);
})
}).catch(function(error) {
console.log('Error while zipping and writing '+JSON.stringify(error));
})
}
这是因为forEach
returnsundefined
,因此Promise.all
立即结算。您应该将其更改为 .map
。
此外,请记住,您的 zipData
论点并非您所期望的那样。此承诺的参数将包含从 zip.file(file, binaryData, {binary: true})
.
返回的每个结果
在这种情况下,您不需要 zipData
。 zip
变量将完成这项工作。在下面的代码中,我还通过删除循环中的冗余承诺并在外部提取一个 .then
来简化承诺链。
var zipFiles = function (filesList) {
var zip = new JSZip();
var zipFilesPromises = filesList.map(function (file) {
return $cordovaFile.readAsBinaryString(cordova.file.dataDirectory + $rootScope.username, file)
.then(function (binaryData) {
return zip.file(file, binaryData, { binary: true });
});
});
return Promise.all(zipFilesPromises)
.then(function () {
return zip.generateAsync({ type: "blob" });
})
.then(function (blob) {
return $cordovaFile.writeFile(cordova.file.dataDirectory + $rootScope.username, 'abc.zip', blob, true);
})
.then(function (data) {
console.log('Zip file written to device at ' + cordova.file.dataDirectory + $rootScope.username);
})
.catch(function (error) {
console.log('Error while zipping and writing ' + JSON.stringify(error));
})
}
我有一个要求需要链接承诺。在我的 Ionic 应用程序中,我需要遍历文件列表并将它们压缩。然后需要将 zip 存储在设备本身上(在本例中为 iPhone)。
我已经有了需要压缩成数组的文件列表。因此,我正在遍历它们并使用 $cordovaFile 获取这些文件的二进制内容。然后我将二进制文件添加到 JSZip 对象。最终结果应该是将所有文件的二进制内容添加到 zip.file,以便生成一个 zip 文件。
//wrapping in Promise.all so that we don't proceed until we have the content of all files added to zip
var zip = new JSZip();
return Promise.all(
filesList.forEach(function(file) {
console.log('file to be added using $cordovaFile '+file);
// Getting the content of each file using $cordovaFile. This returns a promise.
return $cordovaFile.readAsBinaryString(cordova.file.dataDirectory + $rootScope.username, file)
.then(function(binaryData) {
return new Promise(function(resolve, reject) {
//Adding content of all files to zip.file so that it can be zipped in the next step.
resolve(zip.file(file, binaryData, {binary: true}));
})
})
.catch(function(error) {
console.log('Error during fetch content or zipping '+JSON.stringify(error));
})
})
)
一旦 zip.file 拥有所有内容,我将调用 JSZip 中的另一个函数来生成 zip。这也是 return 一个承诺,所以我需要链接到 $cordovaFile.writeFile,以便可以在本地写入 zip。 $cordovaFile.writeFile 也是 return 一个 Promise,它将是链中的最后一个 Promise。
.then(function(zipData) {
// async request to generate the zip
return zipData.generateAsync({type:"blob"});
}).then(function (blob) {
// once we have the zip, save it to the device
$cordovaFile.writeFile(cordova.file.dataDirectory+$rootScope.username, 'abc.zip', blob, true)
.then(function(data) {
console.log('Zip file written to device at '+cordova.file.dataDirectory+$rootScope.username);
})
}).catch(function(error) {
console.log('Error while zipping and writing '+JSON.stringify(error));
})
这是完整代码的样子
var zipFiles = function(filesList) {
var zip = new JSZip();
return Promise.all(
filesList.forEach(function(file) {
return $cordovaFile.readAsBinaryString(cordova.file.dataDirectory + $rootScope.username, file)
.then(function(binaryData) {
return new Promise(function(resolve, reject) {
resolve(zip.file(file, binaryData, {binary: true}));
})
})
.catch(function(error) {
console.log('Error during fetch content or zipping '+JSON.stringify(error));
})
})
)
.then(function(zipData) {
return zipData.generateAsync({type:"blob"});
}).then(function (blob) {
$cordovaFile.writeFile(cordova.file.dataDirectory+$rootScope.username, 'abc.zip', blob, true)
.then(function(data) {
console.log('Zip file written to device at '+cordova.file.dataDirectory+$rootScope.username);
})
}).catch(function(error) {
console.log('Error while zipping and writing '+JSON.stringify(error));
})
}
挑战在于,在 Promise.all 完成后,什么也不会执行。所以,没有任何开始 'then(function(zipData)' 被执行。
我觉得这与我链接 Promises 的方式有关。任何帮助将不胜感激。
Promise.all 从未解析的原因是 filesList.forEach
从未 returns 任何值。
我认为修改为 fileList.map
可以解决您的问题。
所以按如下方式更改您的代码:
var zipFiles = function(filesList) {
var zip = new JSZip();
return Promise.all(
filesList.map(function(file) {
return $cordovaFile.readAsBinaryString(cordova.file.dataDirectory + $rootScope.username, file)
.then(function(binaryData) {
return new Promise(function(resolve, reject) {
resolve(zip.file(file, binaryData, {binary: true}));
})
})
.catch(function(error) {
console.log('Error during fetch content or zipping '+JSON.stringify(error));
})
})
)
.then(function(zipData) {
return zipData.generateAsync({type:"blob"});
}).then(function (blob) {
$cordovaFile.writeFile(cordova.file.dataDirectory+$rootScope.username, 'abc.zip', blob, true)
.then(function(data) {
console.log('Zip file written to device at '+cordova.file.dataDirectory+$rootScope.username);
})
}).catch(function(error) {
console.log('Error while zipping and writing '+JSON.stringify(error));
})
}
这是因为forEach
returnsundefined
,因此Promise.all
立即结算。您应该将其更改为 .map
。
此外,请记住,您的 zipData
论点并非您所期望的那样。此承诺的参数将包含从 zip.file(file, binaryData, {binary: true})
.
在这种情况下,您不需要 zipData
。 zip
变量将完成这项工作。在下面的代码中,我还通过删除循环中的冗余承诺并在外部提取一个 .then
来简化承诺链。
var zipFiles = function (filesList) {
var zip = new JSZip();
var zipFilesPromises = filesList.map(function (file) {
return $cordovaFile.readAsBinaryString(cordova.file.dataDirectory + $rootScope.username, file)
.then(function (binaryData) {
return zip.file(file, binaryData, { binary: true });
});
});
return Promise.all(zipFilesPromises)
.then(function () {
return zip.generateAsync({ type: "blob" });
})
.then(function (blob) {
return $cordovaFile.writeFile(cordova.file.dataDirectory + $rootScope.username, 'abc.zip', blob, true);
})
.then(function (data) {
console.log('Zip file written to device at ' + cordova.file.dataDirectory + $rootScope.username);
})
.catch(function (error) {
console.log('Error while zipping and writing ' + JSON.stringify(error));
})
}