在 JavaScript/TypeSctipt 中创建自定义 Promise 的真实示例

2025-06-07

在 JavaScript/TypeSctipt 中创建自定义 Promise 的真实示例

在 JavaScript 的世界里,我们感觉已经完全从回调转向了Promisesasync/await 。如此之多,以至于几乎每个库和框架都提供了异步版本的函数。其逻辑通常是这样的:“我看到async -> 我输入await并将我的函数也标记为async -> 完成!”。我已经太习惯了,以至于我开始忘记如何创建和使用自己的Promises 了

但生活总会以某种方式提醒你,旧习难改,特别是当你谈论浏览器 API 时。

因此,如果您像我一样,需要一些关于如何做出自定义承诺的提醒,我希望您会发现这篇文章很有帮助。

创建 html 图像。

问题就在这里。我们需要使用浏览器的 API 创建一个 Image 元素,并将其源设置为某个图片的 dataUrl。起初,我是这样写的:

function drawImage(dataUrl: string) {
  const image = new Image();
  image.src = dataUrl;
  return image;
};
Enter fullscreen mode Exit fullscreen mode

看起来不错,但这里有一个问题。当我们设置image.src它时,它不会立即加载。即使源是一个数据字符串,而不是指向外部资源的 URL,它仍然需要时间。因此,当函数返回一张图片时,并不能保证数据已经存在。

不幸的是,如果您想等待加载完成,您就不能做这样的事情。

function drawImage(dataUrl: string) {
  const image = new Image();
  await image.src = dataUrl; // STOP! THIS IS ILLEGAL!
  return image;
};
Enter fullscreen mode Exit fullscreen mode

唯一的方法是设置事件处理程序。但是我们该在这里放什么呢?

function drawImage(dataUrl: string) {
  const image = new Image();
  image.addEventListener('load', () => {
    // ???
  });
  image.src = dataUrl;
  return image;
};
Enter fullscreen mode Exit fullscreen mode

如果我们仍处于 2010 年,我们会通过为回调函数添加另一个参数来解决这个问题。

function drawImage(dataUrl: string, onDone: () => void) {
  const image = new Image();
  image.addEventListener('load', () => {
    onDone();
  });
  return image;
};
Enter fullscreen mode Exit fullscreen mode

等等,都2021年了,现在的年轻人可不会再这么做了。我们需要的是让函数返回一些可等待的东西。没有什么比Promise更可等待的了。这是Promise 的构造函数

function Promise<T>(
  executor: (
    resolve: (value: T) => void, 
    reject: (reason?: any) => void
  ) => void
)
Enter fullscreen mode Exit fullscreen mode

看起来有点吓人,不过你只需要更了解它就行了。通常你都是这么叫它的。

const promise = new Promise((resolve, reject) => {
  // Lalala, do some work
  // ....
  if (we_are_good_and_got_the_result) {
    resolve(result);
  } else {
    reject(error);
  }
})
Enter fullscreen mode Exit fullscreen mode

所以你在构造函数中传递了一个函数。这个函数有两个参数,它们也是函数。分别调用resolvereject。在这个函数中,我们执行一些操作。如果一切顺利,就调用 并resolve传入结果。否则,调用 并传入一个错误对象。
你传入的参数在解析后resolve会以Promise 的形式返回。

然后您就可以使用这个新对象了。

promise.then((result) => {
  // Use the result
}).catch(error => {
  // Oh no there was an error
})
Enter fullscreen mode Exit fullscreen mode

而且,更好的是,你也可以做到这一点。

const result = await promise;
Enter fullscreen mode Exit fullscreen mode

让我们将学到的知识应用到我们的问题中。

function drawImage(dataUrl: string) {
  const promise = new Promise<HTMLImageElement>((resolve, reject) => {
    const image = new Image();
    image.addEventListener('load', () => {
      resolve(image);
    }, { once: true }); // We don't want this handler to run more than once.
    image.src = dataUrl;
  });
  return promise;
}
Enter fullscreen mode Exit fullscreen mode

然后您可以使用 调用新函数await

const image = await drawImage('data');
Enter fullscreen mode Exit fullscreen mode

就这样,我们成功了!

对于那些等不及的人来说,这是一个额外的例子。

这是我再给大家展示一个例子。能够等待一段时间其实挺有用的。可惜的是,JavaScript 中没有wait()orsleep()函数。不过,有了这些新技能,我们可以自己写一个。这里有一个快速的单行代码,大家可以试着做做练习(我感觉自己像个大学老师一样,哈哈)。

const wait = async (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
Enter fullscreen mode Exit fullscreen mode

请向我展示更多,前辈。

本文实际上是我最新博文摘录的扩展版。如果你想了解更多,欢迎查看 ;)。

文章来源:https://dev.to/nordicbeaver/a-real-life-example-of-making-a-custom-promise-in-javascript-typesctipt-5d1n
PREV
软件测试作为文档工具
NEXT
为什么 Websockets 难以扩展?