vmt*_*ran 6 javascript chaining node.js async-await typescript
我正在编写一个模块,旨在在将查询调用到数据库之前准备查询。普通 javascript 中的代码运行得很好,但是当我尝试用 Typescript 编写它时,出现了错误:Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member
我的 JavaScript 代码:
class QueryBuilder {
constructor(query) {
this.query = query;
}
sort(keyOrList, direction) {
this.query = this.query.sort(keyOrList);
return this;
}
skip(value) {
this.query = this.query.skip(value);
return this;
}
limit(value) {
this.query = this.query.limit(value);
return this;
}
then(cb) {
cb(this.query.toArray());
}
}
Run Code Online (Sandbox Code Playgroud)
打字稿中的代码:
class QueryBuilder {
public query: Cursor;
constructor(query: Cursor) {
this.query = query;
}
public sort(keyOrList: string | object[] | object, direction: any) {
this.query = this.query.sort(keyOrList);
return this;
}
public skip(value: number) {
this.query = this.query.skip(value);
return this;
}
public limit(value: number) {
this.query = this.query.limit(value);
return this;
}
public then(cb: Function) {
cb(this.query.toArray());
}
}
Run Code Online (Sandbox Code Playgroud)
我如何调用这些方法:
const query = await new QueryBuilder(Model.find())
.limit(5)
.skip(5)
Run Code Online (Sandbox Code Playgroud)
希望有人能帮助我解决这个问题。提前致谢。
*更新:我从 buitin Promise 扩展了 QueryBuilder 类,然后用 重写了then方法QueryBuilder.prototype.then。代码现在是可执行的,但我并没有真正理解super(executor)构造函数中的内容。这是必需的,executor(resolve: (value?: T | PromiseLike<T> | undefined) => void, reject: (reason?: any) => void): void所以我只是创建了一个愚蠢的执行器。它对代码有何影响?
class QueryBuilder<T> extends Promise<T> {
public query: Cursor;
constructor(query: Cursor) {
super((resolve: any, reject: any) => {
resolve("ok");
});
this.query = query;
}
public sort(keyOrList: string | object[] | object, direction?: any) {
this.query = this.query.sort(keyOrList);
return this;
}
public skip(value: number) {
this.query = this.query.skip(value);
return this;
}
public limit(value: number) {
this.query = this.query.limit(value);
return this;
}
}
QueryBuilder.prototype.then = function (resolve: any, reject: any) {
return resolve(this.query.toArray());
};
Run Code Online (Sandbox Code Playgroud)
用于评估操作数类型的 TypeScript 算法await如下所示(这是一个非常简单的解释)[参考]:
现在知道了这一点,我们可以看到 TypeScript 在检查代码时正在做什么。
\n的操作数await是:
new QueryBuilder(Model.find())\n.limit(5)\n.skip(5)\nRun Code Online (Sandbox Code Playgroud)\nskip不会返回承诺。我们进入步骤 2(注意:调用limit或实例化QueryBuilder都没有)。skipQueryBuilder返回具有可调用成员的实例then。这会导致类型错误:“\'await\' 操作数的类型必须是有效的 Promise,或者不得包含可调用的 \'then\' 成员 (ts1320)”。您的类定义带有可调用的“then”成员:
\nclass QueryBuilder {\n public query: Cursor;\n constructor(query: Cursor) {\n this.query = query;\n }\n \n ...\n \n public then(cb: Function) {\n cb(this.query.toArray());\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n现在我们了解了 TypeScript 是如何抛出类型错误的。但为什么会抛出这个错误呢?JavaScript 可以让你await做任何事情。
\n\nRun Code Online (Sandbox Code Playgroud)\n[rv] = await expression;\n表达式:一个 Promise 或任何要等待的值。
\n
\nrv:返回 Promise 的已实现值,如果不是 Promise,则返回值本身。
\xe2\x80\x93 MDN 文档await
为什么 TypeScript 说“\'await\' 操作数的类型 [如果它不是有效的 Promise] 不得包含可调用的 \'then\' 成员”?为什么它不让你await使用thenable?awaitMDN 甚至给出了一个你可以使用 thenable 的例子。
\n\nRun Code Online (Sandbox Code Playgroud)\nasync function f2() {\n const thenable = {\n then: function(resolve, _reject) {\n resolve(\'resolved!\')\n }\n };\n console.log(await thenable); // resolved!\n}\n\nf2();\n
\xe2\x80\x93 MDN 示例awaiting a thenable
TypeScript 的源代码有有用的注释。上面写着:
\n\n\n该类型不是承诺,因此无法进一步解包。\n只要该类型没有可调用的“then”属性,\n返回该类型就是安全的;否则,会报告错误并返回\nundefined。
\n非承诺“thenable”的一个例子可能是:
\nRun Code Online (Sandbox Code Playgroud)\nawait { then(): void {} }\n“thenable”与 Promise 的最小定义不匹配。\n当 Promise/A+ 兼容或 ES6 Promise 尝试采用此值时,\n该 Promise 将永远不会解决。我们将此视为错误,以帮助标记\n运行时问题的早期指示器。如果用户想要从异步函数返回此值,他们需要将其包装在某个值中。如果他们希望将其视为承诺,则可以将其强制转换为
\n<any>。
\xe2\x80\x93参考
\n通过阅读本文,我的理解是 TypeScript 不适await用于非 Promise thenables,因为它不能保证实现符合Promises/A+定义的最低规范,因此假设它是一个错误。
在您尝试并添加到问题上下文中的解决方案中,您已定义该类QueryBuilder以扩展本机承诺,然后您已覆盖该then成员。虽然这似乎达到了您想要的效果,但存在一些问题:
作为扩展类的结果,您需要先调用其父构造函数,然后才能引用上下文this。父类构造函数的类型是:
(resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void\nRun Code Online (Sandbox Code Playgroud)\n正如您所发现的,您需要传递一些满足该合同的内容,除了让它发挥作用之外没有其他原因。此外,在实例化之后,您的类会返回一个用任意值解析的承诺。无关紧要且不合理的代码是混乱、意外行为的根源,并且可能存在错误。
\n该接口定义了其他方法,例如catch. 该类的使用者可能会尝试使用该catch方法,尽管合同允许他们这样做,但行为并不符合预期。这引出了下一点。
您在问题中提到:
\n\n\n普通 JavaScript 中的代码运行得很好,但是当我尝试用 TypeScript 编写它时,出现了错误
\n
类型的存在是有原因的,其中之一是它们通过强制接口之间的契约来减少错误的可能性。如果您尝试解决它们,则会导致混乱、意外行为和错误风险增加。为什么首先要使用 TypeScript?
\n现在我们了解了错误是什么以及发生的原因,我们可以找到解决方案。该解决方案将要求then成员的实现满足 Promises/A+ 中的最低规范,因为我们已确定这是错误的原因。我们只关心接口的规范then,而不是它的实现细节:
TypeScript 定义then也可供参考(注意:为了便于阅读,我做了一些更改):
/**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\nthen<\n TResult1 = T,\n TResult2 = never\n>(\n onfulfilled?:\n | ((value: T) => TResult1 | PromiseLike<TResult1>)\n | undefined\n | null,\n onrejected?:\n | ((reason: any) => TResult2 | PromiseLike<TResult2>)\n | undefined\n | null\n): Promise<TResult1 | TResult2>;\nRun Code Online (Sandbox Code Playgroud)\n实现本身可能会遵循以下算法:
\n这是示例实现的演示,可以帮助您开始自己的实现。
\nclass CustomThenable {\n async foo() {\n return await \'something\';\n }\n\n async then(\n onFulfilled?: ((value: string) => any | PromiseLike<string>) | undefined | null,\n ): Promise<string | never> {\n const foo = await this.foo();\n if (onFulfilled) { return await onFulfilled(foo) }\n return foo;\n }\n}\n\nasync function main() {\n const foo = await new CustomThenable();\n console.log(foo);\n const bar = await new CustomThenable().then((arg) => console.log(arg));\n console.log(bar);\n}\n\nmain();\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
2093 次 |
| 最近记录: |