我正在尝试创建一个节点js alexa app来控制我的本地kodi实例。我使用Node-Kodi作为JsonRPC API的包装器。在整个应用程序中,我一直在使用Async / await而没有任何问题。我的代码可以在我的公共回购中看到here。
当我尝试执行递归异步功能时,我遇到了问题。
app.intent("AddonInfoTest",
{
"slots": {"ADDON": "ADDON_SLOT"},
"utterances": ["get info for +ADDON+"]
},
async function(request, response) {
let addon = await kodi.addons.getAddonDetails({
'addonid': 'plugin.video.youtube',
'properties': ['path']
});
console.log(addon.addon.path);
let addonMenu = await kodi.files.getDirectory('plugin://plugin.video.youtube');
let menu = buildAddonMenu(addonMenu);
console.log('menu: ' + JSON.stringify(menu));
}
);
function buildAddonMenu(menu, depth = 0)
{
var addonMenu = {};
var subMenu = {};
menu.files.forEach(async function(file) {
addonMenu[file.label] = {
'link': file.file
}
console.log(file);
console.log(depth)
if (file.filetype == 'directory' && !file.label.match(/^next page.*/i) && depth <= constants.addon_menu_max_depth) {
try {
let subMenu = await kodi.files.getDirectory(file.file);
if (typeof subMenu !== 'undefined' && 'files' in subMenu) {
console.log('Building Sub Menu for ' + file.label);
// console.log('SubMenu: ' + JSON.stringify(subMenu));
addonMenu[file.label]['sub_menu'] = buildAddonMenu(subMenu, depth +1);
}
} catch (error) {
console.error(error);
}
}
});
return addonMenu;
}
似乎正在发生的事情是,我只在第一级后得到一个返回值,并且该函数没有等待构建子菜单。
使用youtube插件作为示例,控制台输出如下所示:
[2018-01-02T22:49:58.435Z] { file: 'plugin://plugin.video.youtube/sign/in/',
filetype: 'directory',
label: '[B]Sign In[/B]',
type: 'unknown' }
[2018-01-02T22:49:58.436Z] 0
[2018-01-02T22:49:58.437Z] { file: 'plugin://plugin.video.youtube/special/popular_right_now/',
filetype: 'directory',
label: 'Popular right now',
type: 'unknown' }
[2018-01-02T22:49:58.437Z] 0
[2018-01-02T22:49:58.438Z] { file: 'plugin://plugin.video.youtube/kodion/search/list/',
filetype: 'directory',
label: 'Search',
type: 'unknown' }
[2018-01-02T22:49:58.438Z] 0
[2018-01-02T22:49:58.439Z] { file: 'plugin://plugin.video.youtube/special/live/',
filetype: 'directory',
label: 'Live',
type: 'unknown' }
[2018-01-02T22:49:58.439Z] 0
[2018-01-02T22:49:58.442Z] { file: 'plugin://plugin.video.youtube/config/youtube/',
filetype: 'directory',
label: 'Settings',
type: 'unknown' }
[2018-01-02T22:49:58.442Z] 0
[2018-01-02T22:49:58.442Z] menu: {"[B]Sign In[/B]":{"link":"plugin://plugin.video.youtube/sign/in/"},"Popular right now":{"link":"plugin://plugin.video.youtube/special/popular_right_now/"},"Search":{"link":"plugin://plugin.video.youtube/kodion/search/list/"},"Live":{"link":"plugin://plugin.video.youtube/special/live/"},"Settings":{"link":"plugin://plugin.video.youtube/config/youtube/"}}
[2018-01-02T22:49:59.171Z] Building Sub Menu for Search
[2018-01-02T22:49:59.171Z] { file: 'plugin://plugin.video.youtube/kodion/search/input/',
filetype: 'directory',
label: '[B]New Search[/B]',
type: 'unknown' }
[2018-01-02T22:49:59.171Z] 1
[2018-01-02T22:49:59.171Z] { file: 'plugin://plugin.video.youtube/kodion/search/query/?q=minecraft',
filetype: 'directory',
label: 'minecraft',
type: 'unknown' }
[2018-01-02T22:49:59.171Z] 1
[2018-01-02T22:49:59.172Z] { file: 'plugin://plugin.video.youtube/kodion/search/query/?q=Hello+World',
filetype: 'directory',
label: 'Hello World',
type: 'unknown' }
[2018-01-02T22:49:59.172Z] 1
[2018-01-02T22:49:59.172Z] { file: 'plugin://plugin.video.youtube/kodion/search/query/?q=hello+world',
filetype: 'directory',
label: 'hello world',
type: 'unknown' }
我不是JavaScript而是PHP开发人员,所以我很可能没有正确使用异步函数。请有人指出我正确的方向。
完整的代码可以在这个Branch找到
buildAddonMenu
需要是一个async
函数,并使用await
运算符调用。虽然菜单可能会继续以异步方式构建(因为buildAddonMenu
会返回一个可以在以后更新的对象),但在同步调用buildAddonMenu
后尝试立即渲染或检查菜单会失败 - 您只能获得由提供的数据生成的第一级菜单项在同步通话中。
所以每次打电话给buildAddonMenu
都应该采取这种形式
menuObject = await buildAddonMenu(....)
包括在app.Intent
电话中的那个。
在对上面的控件声明进行更改之后
menu.files.forEach(async function(file) {...}
显然想要为目录中的每个文件执行异步函数。提供的匿名函数确实需要是async
才能在函数体中使用await
- 但forEach
不会等待它对异步函数完成的调用。
Using async/await with a forEach loop有多个解决方案来等待异步调用在循环中完成。如果顺序添加完成的子菜单是可以接受的,那么最简单(和接受)的解决方案可能是创建一个实际的for
循环,它等待处理await
条目的循环体内的异步函数调用(使用menu.files
)。
一般来说,for( ... in ...)
循环返回可枚举的继承属性,不建议通过对象的本地属性进行迭代。使用for( ... of ...)
甚至是标准的for(...;...;...)
循环。