在GTK4和 C中,如何正确等待web_view_javascript_finished() 返回?
我对这里记录的evaluate JavaScript设置实现了一个变体,目的是根据一个函数的结果返回一个字符串:https://webkitgtk.org/reference/webkitgtk/unstable/method.WebView.evaluate_javascript.html
下面是我的变体,请注意文档版本仅仅是打印,而我的版本把user_data设置为指向结果的指针。
web_view_javascript_finish() 运行是异步的,因此在获取值并继续处理流程之前,我需要等待它完成。
我了解过几种等待方法(信号量、互斥锁等),但先尝试一个最简单的方法,运行 while (!result) {},以便结果从NULL变成字符串值时,流程就会继续。
不过,如果去掉这一行,一切按预期工作,也许并不意外,printf("result element: %s\n",result); 打印为null,, 我认为是因为它在JavaScript完成之前就到了那里。
然而,在 while (!result) {},web_view_javascript_finished() 似乎根本没有运行(没有输出)。
在我迈向其他解决方案之前,我想先解决这个问题。
static void
web_view_javascript_finished (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
JSCValue *value;
GError *error = NULL;
value = webkit_web_view_evaluate_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error);
if (!value) {
g_warning ("1 Error running javascript: %s\n", error->message);
g_error_free (error);
return;
}
if (jsc_value_is_string (value)) {
gchar *str_value = jsc_value_to_string (value);
JSCException *exception = jsc_context_get_exception (jsc_value_get_context (value));
if (exception)
g_warning ("2 Error running javascript: %s\n", jsc_exception_get_message (exception));
else
g_print ("3 Script result: %s\n", str_value);
g_free (str_value);
} else {
g_warning ("4 Error running javascript: unexpected return value\n");
}
gchar *str_value = jsc_value_to_string (value);
g_print ("Script result: %s\n", str_value);
user_data = str_value;
g_print ("user_data result: %s\n", user_data
g_object_unref (value);
}
static char *web_view_get_element_info (WebKitWebView *web_view,
const gchar *uuid)
{
gchar *script = g_strdup_printf ("el = window.document.getElementById('%s'); el_rect = el.getBoundingClientRect(); JSON.stringify(el_rect); ");
char *result = NULL;
webkit_web_view_evaluate_javascript (web_view, script, -1, NULL, NULL, NULL, web_view_javascript_finished, &result);
while (!result) {}
printf("result element: %s\n",result);
return web_result;
g_free (script);
}
解决方案
web_view_get_element_info() 和 web_view_javascript_finished() 应该在同一个线程中运行。因此,如果你在其中一个中有一个while循环,只要它在运行,finished() 回调就永远不会执行。
你可以尝试的最小修改是使用 g_main_context_iteration()
所以,不是像
while (!result) {}
你可以尝试像下面这样
while (!result)
g_main_context_iteration (NULL, TRUE);
在这样做时,在 finished() 回调中你始终需要把 result 设置为一个非NULL值(即使发生错误)。否则,它将会无限循环。
另一种建议——在许多基于GObject的项目中常用——是使用 GTask 来完成同样的事情。用 GTask,你会这样做:
#include <gio/gio.h>
static void
my_cb (GObject *obj,
GAsyncResult *result,
gpointer user_data)
{
GTask *task = user_data;
GError *error = NULL;
gpointer ret;
/* Finish the async function to get the result/error */
ret = my_app_execute_finish (MY_APP (obj), result, &error);
if (error)
{
g_task_return_error (task, error);
}
else
{
/* Do something with @ret, if needed */
g_task_return_pointer (task, ret, NULL);
}
}
void some_func(MyApp *self)
{
g_autoptr(GTask) task = NULL;
gpointer value;
task = g_task_new (NULL, NULL, NULL, NULL);
/* Run some Async function with appropriate arguments */
my_app_execute_async (self, NULL, my_cb, task);
while (!g_task_get_completed (task))
g_main_context_iteration (NULL, TRUE);
/* pass error argument, if needed */
/* free @value as needed */
value = g_task_propagate_pointer (task, NULL);
/* Do something with @value */
}