I'm trying to understand the relationship between async
/await
and Promise
s in TypeScript and have gotten myself a but too confused, I think.
Im particular, I'm trying to understand how the async
/await
version of an example function, downloadFileAA
, defined below maps to an implementation that uses Promise
s instead. What is going in behind the scenes with Promise
s? Obviously neither of my Promise
versions are satisfactory or entirely capture what's going on in the async
/await
version (downloadFilePromise
never reports success or failure, and downloadFilePromise2
requires some weird redundancy: result.then()
in the argument to resolve
to pass type checking), but I'm not certain why.
What would a Promise
-based version of downloadFileAA
look like? Is there a reason to prefer it (or not) over the async
/await
version?
import * as FileSystem from 'expo-file-system'
const callback = ( downloadProgress ) => {
const progress = downloadProgress.totalBytesWritten / downloadProgress.totalBytesExpectedToWrite
console.log(
`${Number( progress )
.toLocaleString( undefined, { style: 'percent', minimumFractionDigits: 0 } )
.padStart( 5, ' ' )} of ${downloadProgress.totalBytesExpectedToWrite} bytes`
)
}
const downloadResumable = FileSystem.createDownloadResumable(
'http://...',
FileSystem.documentDirectory 'somebigfile',
{},
callback
)
const downloadFileAA = async () => {
try {
console.log( 'Starting download (with async/await)... ' )
const result = await downloadResumable.downloadAsync()
console.log( result ? `Finished downloading to ${result.uri}` : 'Undefined result?' )
} catch ( e ) {
console.error( `Failed download: ${e.message}` )
}
}
const downloadFilePromise = () => {
console.log( 'Starting download (with Promise)... ' )
new Promise<FileSystem.FileSystemDownloadResult>( ( resolve, reject ) =>
downloadResumable.downloadAsync()
)
.then( ( result ) => console.log( `Finished downloading to ${result.uri}` ) )
.catch( ( reason ) => console.error( `Failed download: ${reason}` ) )
}
const downloadFilePromise2 = () => {
console.log( 'Starting download (with Promise Two)... ' )
new Promise<FileSystem.FileSystemDownloadResult>( ( resolve, reject ) => {
const result = downloadResumable.downloadAsync()
result ? resolve( result.then() ) : reject( result )
} )
.then( ( result ) => console.log( `Finished downloading to ${result.uri}` ) )
.catch( ( reason ) => console.error( `Failed download: ${reason}` ) )
}
CodePudding user response:
Assuming that downloadAsync
returns a Promise and that console.log
doesn't throw, this
const downloadFileAA = async () => {
try {
console.log( 'Starting download (with async/await)... ' )
const result = await downloadResumable.downloadAsync()
console.log( result ? `Finished downloading to ${result.uri}` : 'Undefined result?' )
} catch ( e ) {
console.error( `Failed download: ${e.message}` )
}
}
is equivalent to
const downloadFileAA = () => {
console.log('Starting download (with async/await)... ')
return downloadResumable.downloadAsync()
.then((result) => {
console.log(result ? `Finished downloading to ${result.uri}` : 'Undefined result?')
})
.catch((e) => {
console.error(`Failed download: ${e.message}`);
});
};
What await
does is it replaces .then
. It doesn't replace Promises - both .then
and await
require a Promise (to be used sensibly, at least).
Your downloadFilePromise
function is broken because it constructs a Promise that, by definition, never resolves - you must call the resolve
or reject
argument in order for the constructed Promise to resolve. But since it looks like downloadAsync
already returns a Promise, constructing another Promise to surround it doesn't make any sense - just use the Promise you have instead of making a new one.
Is there a reason to prefer it (or not) over the async/await version?
It's purely a stylistic choice. IMO await
shines best when there are multiple values that need to be waited for (serially), but otherwise either work just fine (as long as they're implemented properly, of course).