【问题标题】:How to use Promise.all() with Typescript如何将 Promise.all() 与 Typescript 一起使用
【发布时间】:2015-11-13 01:44:57
【问题描述】:

这是我想做的:

Promise.all([aurelia.start(), entityManagerProvider.initialize()])
    .then((results:Array<any>) => {
        let aurelia: any = results[0];
        aurelia.setRoot();
    });

aurelia.start() 返回 Aurelia 类型,而 initialize() 返回 void。

编译器给出一个错误消息,指出无法从用法中推断出类型。

我想要实现的是让它们同时运行,因为它们都是非常长的过程,然后运行Aurelia.setRoot();

【问题讨论】:

    标签: typescript


    【解决方案1】:

    通常最好有类型一致的数组。您可以手动执行以下操作(传入通用参数):

    Promise.all<Aurelia, void>(
      [aurelia.start(), entityManagerProvider.initialize()
    ])
    .then(results => {
        let aurelia = results[0];
        aurelia.setRoot();
    });
    

    【讨论】:

    • Visual Studio Code 认可该语法,但我的构建系统中捆绑的 tslint 抱怨:the array is not declared correctly it should be Type[] or Array&lt;Type&gt;。奇怪的是,搜索该错误消息会产生 0 个结果。
    • 所以写let foo : Array&lt;Promise&lt;Aurelia&gt;,Promise&lt;void&gt;&gt; = [aurelia.start(), entityManagerProvider.initialize()];
    • 现在看来这对我来说是纯粹的推理。它返回一种 Promise ,效果很好。我不确定这是由于最近的编译器特性还是 ES6 Promise 的类型现在比以前更好了。无论如何,这里的推论令人印象深刻。
    • -1:通过Promise.all&lt;T, Y, Z, number, ...&gt; 键入比首先声明一个类型化的承诺数组更直接。查看答案:stackoverflow.com/a/40430386/457268
    【解决方案2】:

    由于Promise::all是一个泛型函数,你可以像这样声明每个promise的返回类型:

    Promise.all<Aurelia, void>([
      aurelia.start(),
      entityManagerProvider.initialize()
    ])
    .then(([aurelia]) => aurelia.setRoot());
    

    【讨论】:

    • 在 vscode 中完美运行。但不知何故,当我通过 tsc 编译它时,它说error TS2346: Supplied parameters do not match any signature of call target. (tsc -v 2.0.6)。有什么解决办法吗?
    • 这会产生 “提供的参数与调用目标的任何签名都不匹配。” 在 2.1.4 中也是如此。
    • 在 Typescript 2.7.2 中,我发现它能够正确推断传入 then() 块的数组中的类型,甚至无需将类型“提前”声明为Promise::all 上的通用参数。
    • @JamieBirch 谢谢,它似乎也适用于2.7.1
    【解决方案3】:

    至少从 TypeScript 2.7.1 开始,编译器似乎可以在没有帮助的情况下解析类型,语法如下:

    Promise.all([fooPromise, barPromise]).then(([foo, bar]) => {
      // compiler correctly warns if someField not found from foo's type
      console.log(foo.someField);
    });
    

    帽子提示:@JamieBirch(来自@AndrewKirkegaard 的回答的评论)

    【讨论】:

    • 或者更好的是 const results: [foo, bar] = await Promise.all([fooPromise, barPromise]); 稍微更新一下
    【解决方案4】:

    如果您想保持类型安全,可以扩展 Promise 对象(类型为 PromiseConstructor)的本机类型定义,并为 when @ 添加额外的重载签名987654323@ 使用有限数量的非必要可相互赋值的值调用:

    interface PromiseConstructor
    {
        all<T1, T2>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<[T1, T2]>;
        all<T1, T2, T3>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<[T1, T2, T3]>;
        ...
    }
    

    根据需要添加尽可能多的重载。这种方法为onfulfilled 回调的value 参数中的所有元素提供了完整的类型安全性:

    Promise.all([1, "string", true]).then(value =>
    {
        let a: number = value[0]; // OK
        let b: number = value[1]; // Type 'string' is not assignable to type 'number'.
        ...
    });
    

    【讨论】:

    • 我认为这是一个优雅的解决方案。
    • 当你不知道你会提前多少时,不能很好地扩展
    • @blockhead 如果您需要超过 3-5 个,则最好重组以接收单个对象。这恰好适用于您只需要考虑 一些 值的情况。
    【解决方案5】:

    第 1 部分

    有一些功能你需要了解 A)Promise.all 和 B)Promise.then:

    A)Promise.all的类型定义是一个函数:

        all<T>(values: readonly (T | PromiseLike<T>)[]): Promise<T[]>;
    

    B) Promise.then 的类型定义是一个稍微复杂一点的函数:

        then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
    

    第 1 部分.a

    Promise.then的类型定义很多但是可以分解成小部分:

    then&lt;TResult1 = T, TResult2 = never&gt; 一个函数 then 和 2 个泛型 TResult1, TResult2&lt; &gt; 意味着我们可以稍后在其中设置和使用值——它们被称为generics

    then 函数本身:(onfulfilled?: ..., onrejected?: ...): Promise&lt;TResult1 | TResult2&gt;

    PromiseLike 是一个辅助类型,与Promise 相同(用于介绍课程)。

    onfulfiledonrejected 是以下形式的函数:(value: T) =&gt; (TResult1 OR PromiseLike&lt;TResult1&gt;) OR undefined OR null。注意这里使用了通用的T

    第 2 部分 -

    Promise 本身有一个通用接口:interface Promise&lt;T&gt;&lt;T&gt; 是一个/generic

    所以当你打电话时

    Promise.all<SomeCoolType>([a(), b(), c()]).then( value => doSomething(value) )
    

    你的泛型是SomeCoolType,在这个例子中一些很酷的类型是

    interface SomeCoolType = [A() =&gt; string, B() =&gt; boolean, C() =&gt; number]

    现在请记住,A B C 必须是 Promise。这样一来,.then( value =&gt; ... 中的 value 将成为 SomeCoolType 的结果,对我们来说,它调用了所有这些函数,结果是 [string, boolean, number]

    结论 -

    具体来说,您传递给您的函数/承诺数组Promise.all&lt;T&gt;.then(result =&gt; ...) 中使用的泛型。这些承诺的返回/解析值将成为result 的值/类型。

    示例:Promise.all&lt;[Promise&lt;() =&gt; string&gt;]&gt;([returnStringAsync()]).then(result =&gt; console.log(typeof result === "string")); # =&gt; true

    【讨论】:

      【解决方案6】:

      对于任何想在 React TypeScript 中使用 Promise.all 的人:

      Promise.all<Foo, Bar>(fooPromise, barPromise)
      

      其中fooPromisebarPromise 是您要并行执行的承诺,fooPromise 返回一个foo 类型的响应,barPromise 返回一个bar 类型的响应。

      如果您查看.then 的响应,您会看到响应是一个元组:[Foo, Bar]

      【讨论】:

        【解决方案7】:

        当使用返回值的承诺时,我最喜欢使用Promise.all() 是使用数组解构,如下所示。使用await 更具可读性,并且解构填充了正确的变量。

        export interface IDeveVersionHubDataService {
            getBuildFilterDeveVersions(): Promise<IBuildFilterDeveVersionDto[]>;
            getBuildFilterUsers(): Promise<IBuildFilterUserDto[]>;
        }
        
        const loadDeveVersions = deveVersionHubDataService.getBuildFilterDeveVersions();
        const loadUsers = deveVersionHubDataService.getBuildFilterUsers();
        
        const [deveVersions, users] = await Promise.all([loadDeveVersions, loadUsers]);     
        

        【讨论】:

          【解决方案8】:

          一个完整的例子:

          type returnString = string;
          type returnNumberType = number;
          type jobsType = [Promise<returnString>, Promise<returnNumberType>];
          
          const returnString = async (): Promise<returnString> => {
            return 'This is a string';
          };
          
          const returnNumber = async (): Promise<returnNumberType> => {
            return 123;
          };
          
          const jobs = [];
          
          jobs.push(returnString());
          
          if (Math.random() > 0.5) {
            jobs.push(returnNumber());
          }
          
          (async () => {
            const results = await Promise.all(jobs as jobsType);
            // results now has the correct.
            // tslint:disable-next-line: no-console
            console.log(results);
          })();
          
          

          【讨论】:

            【解决方案9】:

            我在寻找 Promise.all() 的返回类型时不知何故来到了这里,因为简单的 [Promise&lt;any&gt;, Promise&lt;any&gt;] 显然不起作用。

            最终证明它比看起来更简单:

            const severalMongoDbOperations: Promise<[DeleteWriteOpResultObject, UpdateWriteOpResult]> =
              () => Promise.all([
                mongo.deleteOne({ ... }),
                mongo.updateOne({ ... })
              ]);
            

            以后可以和.then()一起使用或者:

            try {
              const ops: [DeleteWriteOpResultObject, UpdateWriteOpResult] = await severalMongoDbOperations();
            } catch (e) {
              // Process rejection
            }
            

            【讨论】:

              【解决方案10】:

              我和你有同样的问题,但是使用这段代码,一切都很好。

              type TList = Promise<Aurelia> | Promise<void>;
              
              const foo: TList[] = [aurelia.start(), entityManagerProvider.initialize()];
              
              Promise.all<TList>(foo).then((results) => {
                  let aurelia = results[0];
                  aurelia.setRoot();
              });
              

              【讨论】:

                【解决方案11】:

                Promise.all 返回一个承诺列表。如果你等待所有的承诺,你可以得到所有承诺的结果。

                        const [tomorrow, today, yesterday] = await Promise.all([
                            fun('tomorrow'),
                            fun('today'),
                            fun('yesterday')
                        ]);
                

                【讨论】:

                • 否:Promise.all 返回“解析为输入承诺结果数组的单个 Promise”(来自 MDN)。此外,这个问题是关于 TypeScript 和不同输入承诺类型的类型推断。
                猜你喜欢
                • 1970-01-01
                • 2019-05-27
                • 2017-11-25
                • 1970-01-01
                • 2020-04-16
                • 2020-04-04
                • 2019-03-19
                • 2020-12-21
                • 1970-01-01
                相关资源
                最近更新 更多