拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Puppeteer Awaitable 类型详解:`T | PromiseLike<T>` 如何支撑整个 API 的同步/异步双形态

Puppeteer Awaitable 类型详解T | PromiseLikeT如何支撑整个 API 的同步/异步双形态【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteerPuppeteer 中大量的 API如evaluate、waitForFunction、Locator 系列的谓词与映射器都要求传入可以是同步值、也可以是异步 Promise的对象或函数这一约定由核心类型AwaitableT统一表达。本文以官方 API 文档中的Awaitable类型为起点结合 puppeteer-core 源码 逐层展开它的定义动机、同族的Awaitable类型家族以及在 Locator、谓词、映射器中的真实使用方式帮助你理解为什么 Puppeteer 的绝大多数回调即异步接口都能同时接受同步与异步实现。一、类型签名一行定义两种形态官方 API 文档对Awaitable的描述非常克制仅给出签名见 docs/api/puppeteer.awaitable.mdexport type AwaitableT T | PromiseLikeT;该定义位于puppeteer-core的公共类型文件中源码位置为 packages/puppeteer-core/src/common/types.ts/** * public */ export type AwaitableT T | PromiseLikeT;从这行定义可以直接读出三个设计要点联合类型而非单一定义AwaitableT是T与PromiseLikeT的联合。也就是说任何接受AwaitableT的 API调用方既可以传一个已就绪的T值也可以传一个尚未 resolve 的 Promise。API 的实现侧用await一次性消化两种形态——对普通值await是恒等操作对 Promise 则是等待。标记为public源码中的 TSDoc 注释将其标注为公开 API这意味着它是 TypeScript 用户在使用 Puppeteer 时会实际接触到的类型它会被传递到page.evaluate、locator等公开签名的位置因此与内部internal类型在语义稳定性上有所区别。使用PromiseLike而非Promise这是整个类型的关键选择下一节展开。二、为什么是PromiseLikeT而不是PromiseTES 标准库中的PromiseLikeT接口lib.es5.d.ts中定义只要求对象拥有一个then方法interface PromiseLikeT { thenTResult1 T, TResult2 never( onfulfilled?: ((value: T) TResult1 | PromiseLikeTResult1) | null, onrejected?: ((reason: any) TResult2 | PromiseLikeTResult2) | null, ): (PromiseLikeTResult1 | TResult2); }选择更弱的结构类型PromiseLike而非具体的Promise构造器带来两个实际好处结构兼容第三方 Promise 实现Q、Bluebird 等第三方 Promise 库、以及各种测试环境中的 mock Promise只要实现了then即可满足Awaitable。对 Puppeteer 这类需要接收用户代码返回值的库来说用户代码里返回什么类型的 Promise 都不该被类型系统拒绝。类型系统层面更宽松原生Promise还携带catch、finally、静态方法等成员用PromiseLike做联合右支意味着只需要可被await这一最小契约这正是Awaitable名称的语义——它描述的不是是什么而是可被等待。需要注意的边界PromiseLike只约束了then的正向路径签名await一个PromiseLike的失败reject行为依然由运行时处理类型上它保证的是可组合、可 await而不保证任何额外的静态能力。三、Awaitable类型家族同一文件中的近亲类型Awaitable并不是孤立存在的。在 types.ts 中与它同文件定义的一组类型构成了一个可等待家族全部以public或internal标注类型签名可见性用途AwaitableTT \| PromiseLikeTpublic值本身可以是同步值或 PromiseAwaitablePredicateT(value: T) Awaitablebooleanpublic谓词函数判断逻辑可以异步AwaitableIterableTIterableT \| AsyncIterableTpublic可迭代对象同步/异步迭代器皆可AwaitableIteratorTIteratorT \| AsyncIteratorTinternal可迭代器的迭代器层面内部使用EvaluateFuncT(...params: InnerParamsT) Awaitableunknownpublicevaluate传入的函数返回值可异步EvaluateFuncWithV, T(...params: [V, ...InnerParamsT]) Awaitableunknownpublic带首参this 绑定的 evaluate 函数其中 AwaitablePredicate 直接建立在Awaitable之上/** * public */ export type AwaitablePredicateT (value: T) Awaitableboolean;这个组合表达了一个 Puppeteer 的常见模式过滤/判断回调既可以是纯同步的(value) boolean也可以是返回Promiseboolean的异步函数例如谓词内部要发起网络请求或调用evaluateHandle。实现侧只需对返回值做一次await即可归一。EvaluateFunc与EvaluateFuncWith见 types.ts则说明Awaitable覆盖了另一大类 APIpage.evaluate/frame.evaluate等接口接受用户函数其返回值声明为Awaitableunknown即你在页面上下文里写的函数可以是 async 的。配合InnerParams对参数做HandleOr扁平化构成了 Puppeteer 求值 API 的完整类型链路。四、实战印证Awaitable在 Locator 与谓词中的落地类型定义本身抽象真正的价值要看它在核心功能中的调用关系。Locator APIPuppeteer 对元素等待与操作的响应式封装是Awaitable最密集的消费方之一相关文件为 packages/puppeteer-core/src/api/locators/locators.ts。4.1page.locator(func)接受返回 Awaitable 的工厂函数Page与Frame都暴露了以函数创建 Locator 的重载// Page.ts L1203 / Frame.ts L543 locatorRet(func: () AwaitableRet): LocatorRet;即 Page.ts 与 Frame.ts。这个重载背后的实现类是FunctionLocatorlocators.tsexport class FunctionLocatorT extends LocatorT { static createRet( pageOrFrame: Page | Frame, func: () AwaitableRet, ): LocatorRet { return new FunctionLocatorRet(pageOrFrame, func).setTimeout( getDefaultTimeout in pageOrFrame ? pageOrFrame.getDefaultTimeout() : pageOrFrame.page().getDefaultTimeout(), ); } // ... _wait(options?: ReadonlyActionOptions): ObservableHandleForT { const signal options?.signal; return defer(() { return from( this.#pageOrFrame.waitForFunction(this.#func, { timeout: this.timeout, signal, }), ); }).pipe(throwIfEmpty()); } }可以看到Awaitable在这里完成了一次类型到行为的映射func: () AwaitableRet这个回调被原样传给waitForFunction由后者负责轮询与等待。也就是说Awaitable类型约定返回值可同步可异步与waitForFunction的运行时行为对每次求值结果做判断与轮询是配套的——类型允许异步运行时才提供轮询兜底。4.2 谓词Predicate同步类型守卫与异步布尔的并集Locator.filter接受的谓词类型定义为locators.tsexport type PredicateFrom, To extends From From ((value: From) value is To) | ((value: From) Awaitableboolean); export type HandlePredicateFrom, To extends From From | ((value: HandleForFrom, signal?: AbortSignal) value is HandleForTo) | ((value: HandleForFrom, signal?: AbortSignal) Awaitableboolean);这是Awaitable参与类型收窄的典型例子联合的左支是 TS 类型守卫value is To用于filter后把LocatorFrom收窄为LocatorTo右支是返回Awaitableboolean的普通函数。由于 TS 的判别联合规则只要谓词声明为类型守卫形式filter就能获得类型收窄能力声明为异步形式则保留完整元素类型允许回调内部执行异步逻辑如检查网络请求、调用evaluateHandle等。4.3 映射器MapperPromise.resolve是 Awaitable 的运行时归一Locator.map的映射函数类型同样建立在Awaitable之上locators.tsexport type MapperFrom, To (value: From) AwaitableTo; export type HandleMapperFrom, To ( value: HandleForFrom, signal?: AbortSignal, ) AwaitableHandleForTo;其运行时消费点在MappedLocator._wait中locators.tsoverride _wait(options?: ReadonlyActionOptions): ObservableHandleForTo { return this.delegate._wait(options).pipe( mergeMap(handle { return from(Promise.resolve(this.#mapper(handle, options?.signal))); }), ); }Promise.resolve(x)是处理AwaitableT的标准手法若 mapper 同步返回HandleForToPromise.resolve原样包装若返回 Promise则直接复用该 Promise。外层再经 rxjs 的from转回 Observable 管道。这段代码直观展示了Awaitable约定的落地范式——类型层允许两种形态实现层用一次Promise.resolve/await归一调用者因此获得写同步回调和写异步回调完全等价的体验。五、对使用者的实际含义结合上述源码Awaitable给最终用户带来的规则可以归纳为三条凡是类型签名里出现AwaitableT的回调/返回值同步与异步写法等价。例如page.locator(async () await this.page.waitForResponse(...))与返回现成值的同步写法都合法filter的谓词里也可以await一个网络请求。返回 async 函数不要求必须可轮询像page.locator(func)这种基于FunctionLocator的实现func最终交给waitForFunction轮询见 4.1 的_wait实现受 Locator 自身timeout默认取页面defaultTimeout约束而evaluate类 API 中的 async 函数则是单次执行后 await 结果两者等待语义不同选型时应注意区分。传递第三方 Promise 实现无需断言由于右支是PromiseLike非原生但实现了then的 Promise 对象可以直接作为Awaitable的取值传入无需as unknown as PromiseT之类的类型断言。六、延伸阅读与参考路径类型定义源头packages/puppeteer-core/src/common/types.tsAwaitableL61、AwaitablePredicateL15、AwaitableIterableL56、EvaluateFuncL99 等本类型官方 API 页docs/api/puppeteer.awaitable.md同族类型页AwaitableIterable、AwaitablePredicate、EvaluateFunc主要消费方Locator 实现 packages/puppeteer-core/src/api/locators/locators.tsPage/Frame的locator重载见 Page.ts 与 Frame.ts。总结来说AwaitableT T | PromiseLikeT虽只有一行却是 Puppeteer 类型体系里同步/异步双形态 API的统一基石向上它派生出AwaitablePredicate、AwaitableIterable、EvaluateFunc等一批公开类型向下它在 Locator 谓词、映射器和waitForFunction的调用链中得到一致的运行时归一处理。理解这一类型基本就理解了 Puppeteer API 中为什么这里能写 async、那里也能不写的全部原因。【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门