Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created June 17, 2021 11:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bennadel/a2fd54618e9d465403071dc9dcfcfd42 to your computer and use it in GitHub Desktop.
Save bennadel/a2fd54618e9d465403071dc9dcfcfd42 to your computer and use it in GitHub Desktop.
Managing And Reporting Errors During Parallel Array Iteration In Lucee CFML 5.3.7.47
<cfscript>
// Let's download these images in parallel.
imageUrls = [
"https://picsum.photos/500/300",
"https://picsum.photos/501/301",
"https://picsum.photos/502/302",
"https://picsum-BAD-DNS.photos/503/303", // This will fail (invalid domain name).
"https://picsum.photos/504/304",
"https://picsum.photos/505/305",
"https://picsum-BAD-DNS.photos/506/306" // This will fail (invalid domain name).
];
results = withTempDirectory(
( tempDirectory ) => {
// Since we know that some downloads may fail (bad files, network errors,
// server errors, etc), we're going to use a MAP instead of an EACH. This
// way, we can create a "result" for each parallel image download operation
// that reports the success or failure.
var iterationResults = imageUrls.map(
( imageUrl, i ) => {
// For this type of "results-based" approach, I like to set up a
// "happy path" result that can be returned AS IS; or, updated in the
// case of an error.
var result = {
success: true,
error: nullValue(),
imageUrl: imageUrl,
imageIndex: i,
imageFilename: "image-#i#.jpg"
};
try {
fileCopy( imageUrl, "#tempDirectory#/#result.imageFilename#" );
} catch ( any error ) {
// Modify the "happy path" result to represent the "sad path".
result.success = false;
result.error = error;
}
return( result );
},
// Parallel iteration.
true,
// Maximum number of threads to use.
10
);
return( iterationResults );
}
);
successCount = countWithProperty( results, "success", true );
errorCount = countWithProperty( results, "success", false );
```
<cfoutput>
<h1>
Download Results
</h1>
<p>
<strong>Success:</strong> #successCount#,
<strong>Error:</strong> #errorCount#
</p>
<cfif errorCount>
<p>
The following images failed to download - re-uploading the images may
fix some of these errors.
</p>
<ul>
<cfloop value="result" array="#results#">
<cfif ! result.success>
<li>
#encodeForHtml( result.imageUrl )#
</li>
</cfif>
</cfloop>
</ul>
</cfif>
</cfoutput>
```
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
/**
* I create a temp directory and pass it to the given operator. Any value returned
* from the operator is automatically passed back to the calling context. The temp
* directory is deleted once the operator finishes executing.
*/
public any function withTempDirectory( required function operator ) {
var path = expandPath( "./temp-#createUniqueId()#" );
directoryCreate( path );
try {
return( operator( path ) );
} finally {
// NOTE: Normally I would delete all the temp directory stuff once I was done
// with it; however, for the demo, I'm going to keep this around.
// --
// directoryDelete( path, true );
}
}
/**
* I count the number of items that have the given key-value pair.
*/
public numeric function countWithProperty(
required array collection,
required string key,
required any value
) {
var count = 0;
for ( var item in collection ) {
if ( item[ key ] == value ) {
count++;
}
}
return( count );
}
</cfscript>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment