简体   繁体   English

在使用GJS进行一系列异步任务之后,我该如何安排某些事情发生?

[英]How can I schedule something to happen after a series of asynchronous tasks with GJS?

I'm writing a simple desktop application in JavaScript with GJS and the GNOME platform: GTK+, GLib, Gio, GObject. 我正在使用JavaScript与GJS和GNOME平台编写一个简单的桌面应用程序:GTK +,GLib,Gio,GObject。 The code below illustrates the situation I'm facing, and is easier to be reproduced because it doesn't need access to the files the application uses. 下面的代码说明了我所面临的情况,并且由于不需要访问应用程序使用的文件,因此更易于复制。 In short, I would like to run the indicated line of code after finishing a series of asynchronous tasks (loading the contents of a series of files). 简而言之,我想完成一系列异步任务(加载一系列文件的内容) 之后运行指定的代码行。 How can I do this in GJS, preferably using something from Gio or maybe from JavaScript itself? 如何在GJS中做到这一点,最好使用Gio或JavaScript本身提供的东西?

 #!/usr/bin/gjs const Lang = imports.lang; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Home = new Lang.Class({ Name: "Home", // Start snippet 1 _enumerateChildrenAsyncCallback: function(dir, result) { let fileEnumerator = dir.enumerate_children_finish(result); let displayName, file, fileInfo, fileType, iter; while ((fileInfo = fileEnumerator.next_file(null))) { iter = this.model.append(); displayName = fileInfo.get_display_name(); this.model.set(iter, [0], [displayName], 1); file = dir.get_child(fileInfo.get_name()); fileType = file.query_file_type(Gio.FileQueryInfoFlags.NONE, null); if (fileType != Gio.FileType.REGULAR) continue; file.load_contents_async(null, function(file, result) { let [success, contents, etag] = file.load_contents_finish(result); let message = ""; if (success) { message = "Finished loading file %s"; } else { message = "Couldn't load file %s"; } log(message.replace("%s", file.get_basename())); }); } }, _init: function() { this.application = new Gtk.Application(); this.application.connect("activate", Lang.bind(this, this._onActivate)); this.application.connect("startup", Lang.bind(this, this._onStartup)); }, _onActivate: function() { this._window.show_all(); }, _onStartup: function() { this.model = new Gtk.ListStore(); this.model.set_column_types([GObject.TYPE_STRING]); let renderer = new Gtk.CellRendererText(); let dir = Gio.file_new_for_path(GLib.get_home_dir()); dir.enumerate_children_async("standard::*", Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_DEFAULT, null, Lang.bind(this, this._enumerateChildrenAsyncCallback), null); /* * I would like this line to be run after all files have been read. * */ this.model.set_sort_column_id(0, Gtk.SortType.ASCENDING); let column = new Gtk.TreeViewColumn({ title: "Files" }); column.pack_start(renderer, true); column.add_attribute(renderer, "text", 0); let view = new Gtk.TreeView({ model: this.model }); view.append_column(column); let scrolled = new Gtk.ScrolledWindow(); scrolled.hscrollbar_policy = Gtk.PolicyType.AUTOMATIC; scrolled.Vscrollbar_policy = Gtk.PolicyType.AUTOMATIC; scrolled.add(view); this._window = new Gtk.ApplicationWindow({ application: this.application, default_height: 300, default_width: 400, title: "Home, sweet home" }); this._window.add(scrolled); } }); let home = new Home(); home.application.run(ARGV); 

PS: In the provided code, running the indicated line of code before finishing all asynchronous tasks doesn't prevent the application from behaving correctly. PS:在提供的代码中,在完成所有异步任务之前运行指定的代码行不会阻止应用程序正常运行。 However, I'd like to sort the list just once in the application startup, instead of sorting the list every time a file is read. 但是,我想在应用程序启动时对列表进行一次排序,而不是每次读取文件时对列表进行排序。 And, of course, knowing how to do it can be helpful in another situations, too. 而且,当然,知道如何做也可以在其他情况下有所帮助。

If I understand correctly what you are trying to do, you need something like Promise.all() . 如果我正确理解您要做什么,则需要类似Promise.all()东西。 Unfortunately, GJS doesn't have a promise library (yet.) 不幸的是,GJS还没有一个Promise库。

I would suggest doing something like this in your enumerate children callback (I've edited some parts out that are not relevant to what I'm trying to illustrate): 我建议在枚举子级回调中执行类似的操作(我已经编辑了一些与我要说明的内容无关的部分):

_enumerateChildrenAsyncCallback: function(dir, result) {
  let fileEnumerator = dir.enumerate_children_finish(result);
  let displayName, file, fileInfo, fileType, iter;

  let pendingOperations = new Set();

  while ((fileInfo = fileEnumerator.next_file(null))) {
    file = dir.get_child(fileInfo.get_name());
    fileType = file.query_file_type(Gio.FileQueryInfoFlags.NONE, null);
    if (fileType != Gio.FileType.REGULAR) continue;

    pendingOperations.add(file);

    file.load_contents_async(null, (file, result) => {
      let [success, contents, etag] = file.load_contents_finish(result);

      pendingOperations.delete(file);
      if (pendingOperations.size === 0)
        this.model.set_sort_column_id(0, Gtk.SortType.ASCENDING);
    });
  }
},

At first glance this might appear to have a race condition between pendingOperations clearing out its first element and having a second element added, but the load_contents_async callbacks won't start getting called until the next iteration of the main loop, so you should be safe there. 乍一看,这似乎在pendingOperations清除第一个元素和添加第二个元素之间pendingOperations竞争条件,但是直到下一次主循环迭代时, load_contents_async回调才会开始被调用,因此在那里您应该很安全。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM