// addons.ts
export interface addon {
name: string;
desc: string;
run: (someparam: any) => void;
}
export function loadaddons(): Array<addon> {
let addons: Array<addon> = [];
fs.readdirSync(path.join(__dirname, "addons"))
.filter((file) => file.endsWith(".js"))
.forEach((file) => {
import(path.join(__dirname, "addons", file)).then((imported) => {
addons.push({
name: imported.addonInfo.name,
desc: imported.addonInfo.desc,
run: imported.startAddon,
});
});
});
return addons;
}
// index.ts
import { loadaddons } from "./addons";
let addons = loadaddons();
addons.forEach((addon) => {
addon.run("someparam");
});
// example addon
export const addonInfo = {
name: "exampleaddon",
desc: "an example addon",
};
export function startAddon() {}
// output
[]
// wanted output
[
{
name: 'exampleaddon',
desc: 'an example addon',
run: [Function: startAddon]
}
]
问题是import()函数未同步,并且在完成将所有插件添加和添加到变量后该函数将不会返回,是的,我可以在.after()中进行返回,但仅当只有一个插件,事实并非如此。等待返回错误,我不知道如何进行;我希望它如何运行:
注意:打字稿新手
答案 0 :(得分:0)
从某种意义上说,TypeScript中的异步函数具有“传染性”:一旦在管道中引入了异步部分,就必须几乎总是使整个管道也处于异步状态。这并不全是坏事,因为有一些不错的工具可以使用异步函数。
假设您希望将loadaddons
函数修改为异步函数。首先要考虑的是类型签名。现在,它不返回类型为Array<addon>
的值,而是返回类型为Promise<Array<addon>>
的值。因此,您的新签名类似于:
function loadaddons(): Promise<Array<addon>> {
// ... to be updated
}
现在,我们需要修改此函数的主体以异步执行。您已经很好地将函数编写为动作的“管道”( read ,然后是 filter ,然后是 act 等),这使得它特别容易转换。考虑一次一次修改管道。
我们首先需要将readdirSync
替换为readdir
。 readdirSync
返回Array<string>
,而readdir
期望函数作为其最终参数;准备好后,它将使用文件名数组调用此函数。我宁愿使用诺言而不是使用回调,幸运的是,将期望回调的函数转换为可返回诺言的函数足够容易。所需要的只是'util'
模块中的promisify
函数。也就是说,promisify(readdir)(path.join(__dirname, 'addons'))
根据需要返回一个承诺。
现在,我们不能仅对filter
返回的值直接调用promisify(readdir)
方法,因为它是一个promise而不是“常规”值。我们可以使用Promise
进行的几乎所有操作都是提供一个函数,以在promise解析为的值上调用该函数。。这涉及假设的假设“假设这个承诺将解析为名为fs
的数组文件名;我想对该数组做什么?”。这是我们的回答方式:
import { promisify } from 'util';
promisify(readdir)(path.join(__dirname, 'addons'))
.then(fs => fs.filter(f => f.endsWith('.js')))
现在我们要去某个地方。该表达式是Promise
,可解析为包含目录中所有JavaScript文件的文件名的数组。剩下要做的就是import
这些文件并从中收集一些信息。
让我们想象一下对单个文件执行此操作涉及什么。假设其文件名为'my-file.js'
。我们想要import
,然后产生一个满足addon
接口的对象。由于import
产生了Promise
,因此我们将提供一个函数,该函数回答假设的假设“假设此承诺将解析为名为contents
的JavaScript文件;我将如何从中提取所需的数据? ”。可能看起来像这样:
import('my-file.js').then(contents => ({
name: contents.addonInfo.name,
desc: contents.addonInfo.desc,
run: contents.startAddon
}));
请注意,此表达式是Promise<addon>
。我们要对它们取一个 Array 并产生一个Promise<Array<addon>>
。事实证明,做这样的事情已经有了一个漂亮的功能!它被称为Promise.all
,它期望的值为Array<Promise<T>>
类型,并产生一个Promise<Array<T>>
。
这里正在起作用:
import { promisify } from 'util';
promisify(readdir)(path.join(__dirname, 'addons'))
.then(fs => fs.filter(f => f.endsWith('.js')))
.then(fs => Promise.all(fs.map(extractAddOn)));
function extractAddOn(filename: string): Promise<addon> {
return import(filename).then(contents => ({
name: contents.addonInfo.name,
desc: contents.addonInfo.desc,
run: contents.startAddon
}));
}
就是这样!
现在,有一些改进:
addon
接口重命名为AddOn
或Addon
。loadaddons
函数重命名为loadAddOns
或loadAddons
。T[]
表示“值数组,每个值的类型为T
”而不是{{1 }}。应用这些更改可将您带到这里:
Array<T>
希望有帮助!