【问题标题】:Reduce on list of promises减少承诺清单
【发布时间】:2020-06-06 07:06:30
【问题描述】:

我有一个应按顺序执行的作业列表。 由于作业需要几秒钟才能完成,因此应该在后台运行。 我认为一份工作可以描述为

interface Job {
    name: string
    execute(): Promise<boolean>
}

我想要一个函数来获取这个作业列表并按顺序执行它们 直到列表完成或一项工作失败或被拒绝,所以基本上:

function executeUntilFailed(jobs: Job[]): Promise<boolean> 
{
    // execute first job
    // if this job
    //   - returns with true: continue with the next job
    //   - returns with false: resolve the promise with false
    //   - got rejected: reject the promise with the reason prefixed with the jobs name
    //
    // if there are no more jobs to do, resolve the promise with true
    //
    // basically it's a reduce operation with starting value of true and 
    // early stops if one job yields false or got rejected
}

我对 Javascript/Typescript 比较陌生,很难实现。

谢谢, 迪特

【问题讨论】:

  • async function executeUntilFailed(jobs: Job[]) { for(const job of jobs) {if (!await job.execute()) {return false};} }。没什么

标签: javascript typescript promise


【解决方案1】:

感谢 Aluan Hadd 和 ehab。

我收集了他们的解决方案,现在有以下代码, 这正是我需要的:

    interface Job {
        name: string
        execute(): Promise<boolean>
    }


    async function executeUntilFailed(jobs: Job[]) {
        for (const job of jobs) {
            try {
                if(!await job.execute()) {
                    return false
                }
            }
            catch (err) {
                throw new Error(`${job.name}: ${err.message}`)
            }
        }
        return true
    }

这里有一些例子

    class JobImpl implements Job {
        constructor(public name: string, private value: boolean, private throwMsg: string|null = null) {}
        execute(): Promise<boolean> {
            console.log(`executing job '${this.name}'`)
            return new Promise((resolve,reject) => {
                setTimeout(()=> {
                    if(this.throwMsg!=null) { reject(this.throwMsg) }
                    else { console.log(`finished job '${this.name}' with result: ${this.value}`); resolve(this.value) }
                }, 1000)
            })
        }
    }

    const successJobs = [
        new JobImpl("a", true),
        new JobImpl("b", true),
        new JobImpl("c", true),
    ]

    const failedJobs = [
        new JobImpl("d", true),
        new JobImpl("e", false),
        new JobImpl("f", true),
    ]

    const throwingJobs = [
        new JobImpl("g", true),
        new JobImpl("g", true, "undefined problem"),
        new JobImpl("i", true),
    ]


    executeUntilFailed(successJobs)
        .then((res) => console.log("resolved", res))
        .catch((err) => console.log("rejected", err))

    executeUntilFailed(failedJobs)
        .then((res) => console.log("resolved", res))
        .catch((err) => console.log("rejected", err))

    executeUntilFailed(throwingJobs)
        .then((res) => console.log("resolved", res))
        .catch((err) => console.log("rejected", err))

<!-- end snippet -->

【讨论】:

    【解决方案2】:

    作为替代方案,您可以创建一个生成器,然后使用 for await ... of 语法:

    function * chainJobs(jobs) {
        for (const job of jobs) {
            yield job.execute().catch(err => new Error(`${job.name}: ${err.message}`));
        }
    }
    
    async function executeUntilFailed(jobs) {
        for await (const result of chainJobs(jobs)) {
            if (!result) return false;
            if (result instanceof Error) throw result;
        }
        return true;
    }
    

    【讨论】:

      【解决方案3】:

      您可以使用 reduce 函数或 for of 循环来实现这一点,我将在 for of 中展示一个实现

      async function executeUntilFailed(jobs) {
        for (const job of jobs) {
          try {
           // notice that if a job resolved with false then it is considered a successful job
           // This is normal and a promise resolved with false should not be considered an error
            await job.execute()
      
            // if u want based on your description to resolve the whole promise with false if one of promises resolved with false you could do
           // const jobResult = await job.execute()
              // if (jobResult === false) {
                 //  return Prmise.resolve(false)
             //  }
          } catch (err) {
            return Promise.reject(new Error(`${job.name}_${err.toString()}`))
          }
        }
        return Promise.resolve(true)
      }
      

      让我们看看函数的实际作用

      
      
      const successJobs = [{
          name: "a",
          execute: () => Promise.resolve(1)
        },
        {
          name: "b",
          execute: () => Promise.resolve(2),
        },
        {
          name: "c",
          execute: () => Promise.resolve(3)
        },
      ]
      
      const failedJobs = [{
          name: "a",
          execute: () => Promise.resolve(1)
        },
        {
          name: "b",
          execute: () => Promise.reject(new Error("undefined problem")),
        },
        {
          name: "c",
          execute: () => Promise.resolve(3)
        },
      ]
      
      async function executeUntilFailed(jobs) {
        for (const job of jobs) {
          try {
            await job.execute()
          } catch (err) {
            return Promise.reject(new Error(`${job.name}_${err.toString()}`))
          }
        }
        return Promise.resolve(true)
      }
      
      
      console.log(
        executeUntilFailed(successJobs)
        .then((res) => console.log("resolved", res))
        .catch((err) => console.log("rejected", err))
      )
      
      
      console.log(
        executeUntilFailed(failedJobs)
        .then((res) => console.log("resolved", res))
        .catch((err) => console.log("rejected", err))
      )

      【讨论】:

      • 您不需要也不应该使用Promise.resolve/reject,因为在async 函数中您可以只使用returnthrow。否则,这是完全正确的。
      • @AluanHaddad 是的,我知道这一点,但我仍然更喜欢明确地说明它们
      • 那么,当您有一个不返回值的async 函数时,您是否以return Promise.resolve(undefined) 结束它?因为如果是这样,你应该这样做。无论如何,这是一种非常不寻常的做法。你为什么这样做?
      • 我强烈反对。 async 的全部要点支持使用标准控制流构造。如果您不使用throw,则不应使用trycatch。不这样做的另一个原因是,即使在本机支持async(包括所有现代浏览器)的平台上,您也将代码耦合到全局Promise 对象,这可能会阻止优化并且肯定会引入复杂性。最后,你会迷惑任何阅读你代码的人
      • 而且,很多时候 async 函数只需 return 就可以了。您不需要明确的返回值。即使没有特定的解析值,Promise 通常也可以用作指示某事何时完成的指标。因此,只需执行 return 或让 async 函数完成都意味着 async 函数的主体已完成,并且之前从它返回的承诺现在可以解决。在您获得更多使用async 函数编程的经验后,您会觉得不用return Promise.resolve(undefined) 就很自然。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-04
      • 2021-07-30
      • 2019-12-21
      • 2016-11-06
      • 1970-01-01
      • 1970-01-01
      • 2015-01-31
      相关资源
      最近更新 更多