[]
        
(Showing Draft Content)

异步公式

异步函数是指任何以非阻塞方式异步或并发地交付其结果的函数。异步函数具有非阻塞性架构,这意味着一个任务的执行并不依赖于另一个任务。任务可以同时运行。通过允许多个计算同时进行,运行异步函数可以提升性能。GcExcel(假设这是某个Excel插件或扩展的名称)通过从 AsyncCustomFunction 类派生函数来使函数能够进行异步计算。evaluateAsync 方法用于异步计算该函数。GcExcel 还在CalcError 枚举中提供了一个枚举值“Busy”,这表明单元格正在计算异步公式。

下面是一个添加和使用自定义异步函数的示例代码:

public class AsyncFunction {

    public static void main(String[] args) {
        // Register Async custom function.
        Workbook.AddCustomFunction(new MyAddFunction());
        
        // Implement the Async custom Function.
        Workbook workbook = new Workbook();
        IWorksheet worksheet = workbook.getWorksheets().get(0);
        worksheet.getRange("A1").setValue(1);
        worksheet.getRange("B1").setValue(2);
        
        // Add the cell values.
        worksheet.getRange("C1").setFormula("=MyAdd(A1,B1)");
        Object value1 = worksheet.getRange("C1").getValue();
        
        // Display result. The result will be "Busy".
        System.out.println(value1);
        Thread.sleep(2000);
        Object value2 = worksheet.getRange("C1").getValue();
        
        // Display result. The result will be "3".
        System.out.println(value2);
    }
}

// Define Async custom function: MyAddFunction.
class MyAddFunction extends AsyncCustomFunction {
    public MyAddFunction() {
        super("MyAdd", FunctionValueType.Number, new Parameter[] { new Parameter(FunctionValueType.Number), new Parameter(FunctionValueType.Number) });
    }

    @Override
    public CompletableFuture<Object> evaluateAsync(Object[] arguments, ICalcContext context) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
            }
            return (double)arguments[0] + (double)arguments[1];
        });
    }
}

限制:

type=danger

AsyncCustomFunction 的参数不接受任何引用类型,因为异步函数可能会在另一个线程中运行,如果你使用引用,这会导致多线程冲突。同样,在异步函数内部不允许使用如 IWorksheet 和 IWorkbook 这样的对象。

异步 IMAGE 函数

GcExcel 支持异步的 IMAGE 公式函数,允许你从 URL 路径向单元格中添加图片。它提供了 alt_textsizingheightwidth 等参数来设置替代文本、图片大小、高度和宽度。这个函数的 source 参数允许你使用 “https” 协议提供图片的URL路径。为了处理这个网络请求,GcExcel 提供了 Workbook 类的 setWebRequestHandler 方法,使你能够访问和自定义应用程序的网络请求处理器。 setWebRequestHandler 方法是 IWebRequestHandler 接口类型的,这个接口定义了处理网络请求的方式,并提供了一种向指定 URI 发送 GET 请求的方法。

异步函数以非阻塞方式异步或并发地交付结果,但有些操作依赖于其他函数的计算结果。GcExcel 提供了 IWorkbook 接口的 waitForCalculationToFinish 方法,确保在继续依赖计算结果的任何其他操作之前,所有的必要计算都已完成。它会等待所有计算完成,包括异步计算。这个方法会阻塞当前线程直到所有计算完成。

以下是添加和使用异步 IMAGE 函数的示例代码:

public class AsyncImageFunction {

    public static void main(String[] args) throws Exception {
        // Set a custom web request handling class to send all network requests.
        Workbook.setWebRequestHandler(new WebRequestHandler());
        
        // Initialize Workbook.
        Workbook workbook = new Workbook();
        
        // Get the active sheet.
        IWorksheet sheet = workbook.getActiveSheet();
                
        // Set IMAGE function.
        IRange range = sheet.getRange("A1:F10");
        range.setFormula("=IMAGE(\"https://support.content.office.net/en-us/media/926439a2-bc79-4b8b-9205-60892650e5d3.jpg\")");
        
        // Calculate all formulas so the asynchronous image function will run.
        workbook.calculate();
        // Block the current thread until all asynchronous functions have finished to avoid #BUSY! error in the exported file.
        workbook.waitForCalculationToFinish();
        
        // Save the workbook.
        workbook.save("AsyncImageFunction.pdf");
        workbook.save("AsyncImageFunction.xlsx");
    }
}

//Create custom web request handling class.
class WebRequestHandler implements IWebRequestHandler {
    @Override
    public CompletableFuture<WebRequestResult> getAsync(String requestUri) {
        WebRequestResult result = new WebRequestResult();
        return CompletableFuture.supplyAsync(() -> {
            try {
                URL url = new URL(requestUri);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                int responseCode = connection.getResponseCode();
                result.setStatusCode(responseCode);
                if (responseCode >= 200 && responseCode < 300) {
                    result.setResponseContent(toByteArray(connection.getInputStream()));
                } else {
                    System.out.println("Request failed with status code: " + responseCode);
                }
            } catch (IOException e) {
                result.setConnectionFailed(true);
            }
            return result;
        });
    }
    public byte[] toByteArray(InputStream inputStream) {
        try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                output.write(buffer, 0, bytesRead);
            }
            return output.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException("Error converting InputStream to byte array", e);
        }
    }
}