0

设置

我正在尝试设置一个简单的前端来显示来自 Tau Prolog 知识库的数据。现在,我有一个 JS 文件,它使用回调来进行 Prolog 查询并获得答案,输出数据库中所有字符和位置的列表:(您可以忽略过滤字符串的东西,它用于搜索实现,但被初始化''为现在将显示整个列表)

phandelver-prolog.js

session = pl.create();
session.consult("phandelver.prolog");
var bindings = [];
var filterString = '';

// Gets a list of all the characters and prints them out
function print_characters() {
    var print_bindings = function(bindings) {
        for(var i = 0; i < bindings.length; i++) {
            print_character(bindings[i]);
        }
    }
    bindings = [];
    session.query("first_name(Char, FirstName), last_name(Char, LastName).");
    session.answers(get_callback(print_bindings));
}

// outputs a single character if they are found in the search
function print_character(binding) {
    if (binding != null) {
        // Look up term that has been bound to variable "Char"
        var firstName = binding.lookup("FirstName"); 
        var charFirstName = firstName.toString(); // Turn the Term into a string.
        var lastName = binding.lookup("LastName"); 
        var charLastName = lastName.toString(); // Turn the Term into a string.
        var charName = charFirstName.capitalize() + " " + charLastName.capitalize(); 
        // Check if the character matches the search
        if (charName.toLowerCase().match(filterString.toLowerCase())) {
            var names_output = document.getElementById("names_output");
            names_output.innerHTML = names_output.innerHTML + "<div>" + charName +  "</div>"; // Add name to HTML page
        }
    }
}



// Gets a list of all the locations and prints them out
function print_locations() {
    var print_bindings = function(bindings) {
        for(var i = 0; i < bindings.length; i++) {
            print_location(bindings[i]);
        }
    }
    bindings = [];
    session.query("location_name(Loc, Name).");
    session.answers(get_callback(print_bindings));
}

// outputs a single location if they are found in the search
function print_location(binding) {
    if (binding != null) {
        // Look up term that has been bound to variable "Name"
        let name = binding.lookup("Name"); 
        let locName = name.toString(); // Turn the Term into a string.
        // Check if the location matches the search
        if (locName.toLowerCase().match(filterString.toLowerCase())) {
            var names_output = document.getElementById("locations_output");
            locations_output.innerHTML = locations_output.innerHTML + "<div>" + locName +  "</div>"; // Add name to HTML page
        }
    }
}

/*
 * Returns a callback function to pass to session.answers(). 
 * The parameter is the function for the callback to call (with the bindings as parameter)
 * when prolog has found all the answers. 
*/
function get_callback(funcWhenDone) {
    var callbackFunc = function(answer) {
        if (answer == false) {
            funcWhenDone(bindings);
        }
        else {
            // We've gotten another non-false answer - add it to the bindings.
            bindings.push(answer);
        }
    }

    return callbackFunc;
} 

这是一个示例 Prolog 文件:

phandelver.prolog

:- set_prolog_flag(double_quotes, atom).

% The characters in the world.
:- dynamic(character/1).
character(toblin_stonehill).
character(elmar_barthen).

:- dynamic(first_name/2).
first_name(toblin_stonehill, toblin).
first_name(elmar_barthen, elmar).

:- dynamic(last_name/2).
last_name(toblin_stonehill, stonehill).
last_name(elmar_barthen, barthen).

% The locations in the world.
:- dynamic(location/1).
location(stonehill_inn).
location(lionshield_coster).

:- dynamic(location_name/2).
location_name(stonehill_inn, "The Stonehill Inn").
location_name(lionshield_coster, "Lionshield Coster").

以及 index.html 的相关部分(捆绑只是一个 browserify 导入):

索引.html

<head>
  <script src="tau-prolog.js" defer></script>
  <script src="bundle.js" defer></script>
  <script src="phandelver-prolog.js" defer></script>
</head>

<body>
    <main>
        <!-- Where names will be outputted --> 
        <div class="output" id="names_output"></div>
        <!-- Where locations will be outputted --> 
        <div class="output" id="locations_output"></div>
    </main>
</body>

问题

如果我只是调用print_characters()print_locations()从内部调用,所有这些都可以正常工作phandelver-prolog.js,适当地输出字符列表或位置列表。当我尝试同时执行这两个函数调用时,问题就出现了。例如,将其放在 JS 文件的顶部:

phandelver-prolog.js

print_characters();
print_locations();

执行时在 JS 浏览器控制台中出现错误:

Uncaught TypeError: firstName is null

尽管列为 null 的变量的名称会根据我是运行print_characters()还是print_locations()先运行而变化。我认为该错误是 JavaScript 处理同步和回调方式的问题——不同的数据库查询相互干扰,并尝试使用第一个函数的绑定执行第二个函数的查询。

我试图通过使用回调调用print_characters()和来解决这个问题print_locations()

phandelver-prolog.js

updateCharacterUI(updateLocationUI);

function updateCharacterUI(myCallback) {
    print_characters();
    myCallback();
}

function updateLocationUI() {
    print_locations();
}

但我仍然得到同样的错误。我还尝试使用 Mutex 一次调用一个函数(使用 browserify 导入所需的文件):

phandelver-prolog.js

var Mutex = require('async-mutex').Mutex;
var Semaphore = require('async-mutex').Semaphore;
var withTimeout = require('async-mutex').withTimeout;

async function execute(mutex) {
    mutex
    .runExclusive(function() {
        print_characters();
    })
    .then(function(result) {
        print_locations();
    });
}

execute(mutex);

但同样,得到同样的错误。请注意,如果我执行类似调用 print_characters() 之类的操作,然后为第二个函数再次调用 print_characters(),则回调和 Mutex 函数工作正常,两次打印出字符列表。因此,这很可能是由于调用两个不同的查询和 JavaScript 试图同时处理它们而导致的。

我的理解是回调或互斥锁应该处理这样的情况并防止在运行这些函数时发生冲突?print_characters()我认为如果 JS在运行之前完整地运行第一个调用,问题将得到解决print_locations(),但如果我理解正确,那应该是回调/互斥体中发生的事情。

对此的任何帮助将不胜感激!

4

0 回答 0