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. 接下来,我们定义了两个变量 resolvereject,它们是函数类型并具有相应的参数和返回值类型。这些函数将用于解决(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 构造函数中,我们接收两个参数 resrej,它们分别是用于解决和拒绝 Promise 的函数。

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

  1. 最后,我们返回一个包含 Promise 及其相应的 resolvereject 函数的数组。
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 函数来确保 maxTimedelayTime 的值不小于 0 或 1。我们还声明了一些变量,包括 time(当前重试的次数),isCancel(是否取消重试),reject(用于拒绝 Promise 的函数),resolve(用于解决 Promise 的函数),isFinish(是否已完成重试)。

我们还定义了一个名为 isNeedRetry 的函数,用于判断是否需要继续重试。它检查 time 是否小于 maxTimeisCancel 是否为 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. 接着我们定义了 cancelgetResult函数的其余部分:
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. 后,我们返回一个对象,包含 cancelgetResult 方法。getResult 方法返回一个 Promise,用于获取重试的结果。在 getResult 方法内部,我们重新定义了 rejectresolve 函数,然后调用 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时代会更加突出。

写在最后

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

相关推荐
小政爱学习!20 分钟前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
魏大帅。26 分钟前
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
前端·javascript·ajax
花花鱼32 分钟前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k093335 分钟前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
EricWang13581 小时前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
September_ning1 小时前
React.lazy() 懒加载
前端·react.js·前端框架
web行路人1 小时前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
超雄代码狂1 小时前
ajax关于axios库的运用小案例
前端·javascript·ajax
长弓三石2 小时前
鸿蒙网络编程系列44-仓颉版HttpRequest上传文件示例
前端·网络·华为·harmonyos·鸿蒙
小马哥编程2 小时前
【前端基础】CSS基础
前端·css