real跟你侃代码 - 聊聊createPromise

简介: JavaScript是一门灵活而强大的编程语言,它允许开发人员使用各种技巧来创建功能强大且有趣的代码。本文将深入探讨与梳理createPromise/createRetryPromise 的工具函数实现思路。

1. 理解createPromise函数

createPromise函数是一个JavaScript函数,它接受一个包含多个参数的对象作为输入,并返回一个具有cancel和getResult方法的对象。该函数的主要功能是创建一个可重试的Promise。

2. 实现一个

2.1 简单易用版

typescript 复制代码
export function createPromise<T = void>(): [Promise<T>, (value: T) => void, (reason?: unknown) => void] {
  let resolve: (value: T) => void = () => {
    //  init resolve
  };
  let reject: (reason?: unknown) => void = () => {
    //  init reject
  };
  const promise = new Promise<T>((res, rej) => {
    resolve = res;
    reject = rej;
  });
  return [promise, resolve, reject];
}

下面是分步的解析

  1. 首先,这段代码是一个用于创建 Promise 的函数。它返回一个包含 Promise 及其 resolve 和 reject 函数的数组。让我们一步步来理解它。
typescript 复制代码
export function createPromise<T = void>(): [Promise<T>, (value: T) => void, (reason?: unknown) => void] {

这是一个导出的函数,可以通过 createPromise() 调用。它使用了泛型 <T>,其中 T 是 Promise 的值的类型,默认为 void,也就是没有返回值。

  1. 接下来,我们定义了两个变量 resolve 和 reject,它们是函数类型并具有相应的参数和返回值类型。这些函数将用于解决(resolve)或拒绝(reject)Promise。
typescript 复制代码
  let resolve: (value: T) => void = () => {
    //  init resolve
  };
  let reject: (reason?: unknown) => void = () => {
    //  init reject
  };

在这里,我们只是为这些函数提供了空的初始实现。我们稍后将在 Promise 构造函数中重新赋值这些函数,以便在需要时调用它们。

  1. 接下来,我们创建了一个新的 Promise,并将其赋值给变量 promise。
typescript 复制代码
  const promise = new Promise<T>((res, rej) => {
    resolve = res;
    reject = rej;
  });

在 Promise 构造函数中,我们接收两个参数 res 和 rej,它们分别是用于解决和拒绝 Promise 的函数。

在这里,我们将外部的 resolve 函数赋值为 res,将外部的 reject 函数赋值为 rej。这样做是为了将这些函数与新创建的 Promise 关联起来,以便在需要时调用它们。

  1. 最后,我们返回一个包含 Promise 及其相应的 resolve 和 reject 函数的数组。
typescript 复制代码
  return [promise, resolve, reject];
}

现在,您可以使用这个函数来创建一个 Promise,并使用返回的数组中的函数来解决或拒绝该 Promise。

  1. 以下是一个示例用法:
typescript 复制代码
const [myPromise, myResolve, myReject] = createPromise<number>();

myPromise.then((value) => {
  console.log('Promise resolved with value:', value);
}).catch((reason) => {
  console.log('Promise rejected with reason:', reason);
});

// 解决 Promise
myResolve(42);

// 拒绝 Promise
myReject('Something went wrong.');

2.2 chromium 源码扩展版

github.com/chromium/ch...

实现一个自己的util : createPromise.ts

typescript 复制代码
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview PromiseResolver is a helper class that allows creating a
 * Promise that will be fulfilled (resolved or rejected) some time later.
 *
 * Example:
 *  const resolver = new PromiseResolver();
 *  resolver.promise.then(function(result) {
 *    console.log('resolved with', result);
 *  });
 *  ...
 *  ...
 *  resolver.resolve({hello: 'world'});
 */

export class PromiseResolver<T> {
  private resolve_: (arg: T) => void = () => {};
  private reject_: (arg: any) => void = () => {};
  private isFulfilled_: boolean = false;
  private promise_: Promise<T>;

  constructor() {
    this.promise_ = new Promise((resolve, reject) => {
      this.resolve_ = (resolution: T) => {
        resolve(resolution);
        this.isFulfilled_ = true;
      };
      this.reject_ = (reason: any) => {
        reject(reason);
        this.isFulfilled_ = true;
      };
    });
  }

  /** Whether this resolver has been resolved or rejected. */
  get isFulfilled(): boolean {
    return this.isFulfilled_;
  }

  get promise(): Promise<T> {
    return this.promise_;
  }

  get resolve(): ((arg: T) => void) {
    return this.resolve_;
  }

  get reject(): ((arg?: any) => void) {
    return this.reject_;
  }
}

export function createPromise<T>(){
    return new PromiseResolver<T>();
}

2.3 createRetryPromise 增加任务状态标识与重试机制

2.3.1 先给出完整的实现

typescript 复制代码
export const createRetryPromise = <T = any>(opt: {
  runTask: () => Promise<T>;
  checkSuccess: (info: T, time: number) => boolean;
  time: number;
  delayTime?: number;
  onTry?: (time: number) => void;
}) => {
  const maxTime = Math.max(1, opt.time);
  const delayTime = Math.max(0, opt.delayTime || 0);
  let time = 0;
  let isCancel = false;
  let reject: (reason?: any) => void = () => {};
  let resolve: (value?: T) => void = () => {};
  let isFinish = false;

  const isNeedRetry = () => time < maxTime && !isCancel && !isFinish;

  const run = () => {
    if (isCancel) return;
    opt.onTry?.(time);

    opt.runTask()
      .then(message => {
        if (isCancel) return;
        if (opt.checkSuccess(message, time)) {
          time = maxTime;
          resolve(message);
        } else {
          time++;
        }
      })
      .catch(() => {
        time++;
      })
      .finally(() => {
        if (isNeedRetry()) {
          setTimeout(run, delayTime);
        } else {
          reject('timeout');
        }
      });
  };

  const cancel = () => {
    isCancel = true;
    reject('cancel');
  };

  return {
    cancel,
    getResult: () => new Promise<T>((res, rej) => {
      reject = rej;
      resolve = res;
      run();
    }).finally(() => {
      isFinish = true;
    }),
  };
};

2.3.2 逐步解析

  1. 定义函数的签名和参数:
typescript 复制代码
export const createRetryPromise = <T = any>(opt: {
  runTask: () => Promise<T>;
  checkSuccess: (info: T, time: number) => boolean;
  time: number;
  delayTime?: number;
  onTry?: (time: number) => void;
}) => {

这个函数名为 createRetryPromise,它接收一个 opt 参数,该参数是一个对象,包含以下属性:

  • runTask:一个返回 Promise 的函数,表示要执行的任务。
  • checkSuccess:一个函数,用于判断任务返回值是否需要继续执行。它接收两个参数,info 表示任务返回的信息,time 表示当前重试的次数。
  • time:重试的次数。
  • delayTime(可选):延迟时间。
  • onTry(可选):一个函数,表示在每次重试时要执行的操作。它接收一个参数 time,表示当前重试的次数。
  1. 我们定义一些变量和函数:
typescript 复制代码
  const maxTime = Math.max(1, opt.time);
  const delayTime = Math.max(0, opt.delayTime || 0);
  let time = 0;
  let isCancel = false;
  let reject: (reason?: any) => void = () => {};
  let resolve: (value?: T) => void = () => {};
  let isFinish = false;

  const isNeedRetry = () => time < maxTime && !isCancel && !isFinish;

在这里,我们使用 Math.max 函数来确保 maxTime 和 delayTime 的值不小于 0 或 1。我们还声明了一些变量,包括 time(当前重试的次数),isCancel(是否取消重试),reject(用于拒绝 Promise 的函数),resolve(用于解决 Promise 的函数),isFinish(是否已完成重试)。

我们还定义了一个名为 isNeedRetry 的函数,用于判断是否需要继续重试。它检查 time 是否小于 maxTime,isCancel 是否为 false,以及 isFinish 是否为 false。

  1. 我们定义了 run 函数:
typescript 复制代码
  const run = () => {
    if (isCancel) {
      return;
    }
    opt.onTry?.(time);

    opt.runTask()
      .then(message => {
        if (isCancel) {
          return;
        }
        if (opt.checkSuccess(message, time)) {
          time = maxTime;
          resolve(message);
        } else {
          time++;
        }
      })
      .catch(() => {
        time++;
      })
      .finally(() => {
        if (isNeedRetry()) {
          setTimeout(run, delayTime);
        } else {
          reject('timeout');
        }
      });
  };

在 run 函数中,我们首先检查是否已取消重试,如果是,则直接返回。然后,如果定义了 onTry 函数,则调用它并传递当前的重试次数 time。

接下来,我们调用 opt.runTask() 执行任务,并处理返回的 Promise。在 then 方法中,我们检查是否已取消重试,如果是,则直接返回。然后,我们使用 opt.checkSuccess 函数判断任务返回值是否满足成功的条件。如果满足条件,我们将 time 设置为 maxTime,并使用 resolve 函数解决 Promise,传递任务的返回值 message。否则,我们增加 time 的值。

在 catch 方法中,我们增加 time 的值,以捕获可能的错误。

最后,在 finally 块中,我们检查是否需要继续重试。如果是,我们使用 setTimeout 函数延迟一段时间后再次调用 run 函数。否则,我们使用 reject 函数拒绝 Promise,并传递 'timeout' 作为拒绝的原因。

  1. 接着我们定义了 cancel 和 getResult函数的其余部分:
typescript 复制代码
  const cancel = () => {
    isCancel = true;
    reject('cancel');
  };

  return {
    cancel,
    getResult: () => new Promise<T>((res, rej) => {
      reject = rej;
      resolve = res;
      run();
    }).finally(() => {
      isFinish = true;
    }),
  };
};

cancel 函数用于取消重试。它将 isCancel 设置为 true,并使用 reject 函数拒绝 Promise,传递 cancel作为拒绝的原因。

  1. 后,我们返回一个对象,包含 cancel 和 getResult 方法。getResult 方法返回一个 Promise,用于获取重试的结果。在 getResult 方法内部,我们重新定义了 reject 和 resolve 函数,然后调用 run 函数开始执行重试。最后,我们使用 finally 方法,在 Promise 完成后将 isFinish 设置为 true。

3. 实用案例

为了更好地理解createPromise函数的实际应用,我们将以一个场景为例:网络请求的重试机制。 在现代的Web应用程序中,网络请求往往是必不可少的一部分。然而,由于网络不稳定或服务器响应缓慢等原因,请求可能会失败。为了提高可靠性,我们经常需要实现一个网络请求的重试机制。 使用createPromise函数,我们可以轻松地实现这样的重试机制。以下是一个示例代码:

typescript 复制代码
const fetchWithRetry = createRetryPromise({
  runTask: () => fetch('https://api.example.com/data'), // 发起网络请求
  checkSuccess: (response, time) => response.ok || time >= 3, // 判断请求是否成功
  time: 5, // 最大重试次数
  delayTime: 1000, // 每次重试之间的延迟时间(1秒)
  onTry: (time) => console.log(`Retrying... (attempt ${time})`), // 输出重试信息
});

fetchWithRetry.getResult()
  .then(response => response.json())
  .then(data => console.log('Data:', data))
  .catch(error => console.error('Error:', error));

在这个例子中,我们使用createRetryPromise函数创建了一个具有重试功能的网络请求。如果请求成功(response.ok为true),或者重试次数超过3次,请求将被视为成功完成。否则,将在1秒后进行下一次重试。

通过使用createPromise函数,我们可以更好地处理网络请求的失败情况,并在必要时进行重试,提高应用程序的可靠性和用户体验,这点在AI时代会更加突出。

写在最后

很久没在掘金输出过内容了,平时工作真的是特别忙,但是也是想和五湖四海的朋友多多交流,于是想开个自己的小专栏,会不定期的分享一些自己觉得有意思的小知识,或者小感悟吧 嗯,青春可以逝去,我们永远热烈 ❤️‍🔥❤️‍🔥🥤😎

相关推荐
lpfasd1234 小时前
node-sass 停止维护!老项目迁移 Dart Sass 完整教程
前端·rust·sass
Data analyse4567 小时前
rnweb 回放数据量太大怎么办?分片存储与断点续传的实现思路
前端·网络·算法·数据分析
xiaogai_gai7 小时前
金蝶收料通知单查询策略实战教程:从源系统拉取到中间层落地的完整路径
java·前端·数据库
环境栈笔记8 小时前
指纹浏览器 WebRTC 怎么设置?从 ICE 候选、代理出口到功能验证
前端·人工智能·后端·自动化
赵大仁8 小时前
官网性能优化实战:LCP 3.2s → 2.1s、CLS 0.107 → 0,以及被我推翻的 6 个判断
前端·性能优化·cls·lcp·core web vitals
xiancai_xianyu8 小时前
Text2SQL落地生产库前,SQL要过哪三道闸
前端·数据库·sql·sql注入·text2sql·ai agent开发·企业级ai框架
小羊没烦恼!9 小时前
T-SQL查询进阶--理解SQL SERVER中的分区表
服务器·开发语言·前端·javascript·html
史瓦辛格9 小时前
给 DeepSeek Harness 写费用插件:把「这次会话花了多少钱」算准
前端·deepseek
星光不问赶路人9 小时前
Vue 3 + TypeScript 中 enum 的使用实践
前端·vue.js
鸥梨菌Honevid9 小时前
UVM 中 REQ 和 RSP 类型详解:从通信模型到 item_done 与 put_response
java·前端·servlet