关于 Promises、Thenables 和惰性求值你需要知道的一切

本文介绍了Promise的工作原理,特别是关于它们的即时初始化特性,以及这种特性可能导致的意外行为。然后,文章提出了Thenables的概念,并展示了如何创建惰性求值的LazyPromise类,以在真正需要时才执行异步操作。最后,通过示例说明了惰性求值在数据库查询和HTTP请求等场景中的应用价值。
摘要由CSDN通过智能技术生成

关于 Promises、Thenables 和惰性求值你需要知道的一切

本文译自:Everything You Need to Know About Promises, Thenables, & Lazy-Evaluation

译者按:js Promises sleep 失效、未按照预期时间执行,解决方法及原因。

新的一年开始了,虽然很多人都承诺要更加活跃,但我将向您展示如何让 Promise我们变得更懒惰…… Promises。

稍后会更有意义。

首先,让我们看一个基本的 Promise 例子。在这里,我有一个名为 sleep 的函数,它需要以毫秒为单位的时间和一个值。它返回一个承诺,该承诺将执行 setTimeout 我们应该等待的毫秒数;然后 Promise 使用值解析。

/** * @template ValueType * @param {number} ms * @param {ValueType} value * @returns {Promise<ValueType>} */
function sleep(ms, value) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(value), ms);
  });
}

它是这样工作的:

在这里插入图片描述

带有代码“await sleep(1000, ‘Yawn & stretch’)”的 JavaScript 控制台。然后一秒钟后,“‘打哈欠和伸展’”

我们可以 使用参数 和 等待sleep 函数, 一秒钟后, 将记录字符串“Yawn & stretch”。1000``'Yawn & stretch'``console

这没什么特别的。它的行为可能与您预期的一样,但是如果我们将它存储为变量以供 await 稍后使用,而不是 立即await返回 ,它会变得有点奇怪Promise

const nap = sleep(1000, 'Yawn & stretch')

现在,假设我们做一些其他需要时间的工作(比如输入下一个示例),然后 awaitnap 变量。

在这里插入图片描述

在 JavaScript 控制台中输入“await nap”并立即看到响应“‘Yawn & stretch’”

您可能希望在解析之前延迟一秒钟,但实际上它会立即解析。每当您创建一个 Promise时,您都会实例化它所负责的任何异步功能。

在我们的示例中,当我们定义 nap 变量时, Promise 将创建执行 setTimeout. 因为我是一个慢打字者,我们 Promise 会解决的 await

换句话说, Promises 是渴望的。他们不等你给 await 他们。

在某些情况下,这是一件好事。在其他情况下,它可能会导致不必要的资源使用。对于这些场景,您可能想要一些看起来像 Promise,但使用 惰性评价 仅在需要时实例化。

在我们继续之前,我想向您展示一些有趣的东西。

Promises 不是唯一可以 await在 JavaScript 中编辑的东西。如果我们 Object 用一个 .then() 方法创建一个 plain,我们实际上可以 await 像任何 Promise.

在这里插入图片描述

JavaScript 控制台显示文本“await { then: () => console.log(‘🙃’) }”,后跟“🙃”。

这有点奇怪,但它也允许我们创建 看起来Promises 但不是的不同对象。这些对象有时被称为“然后能“.

考虑到这一点,让我们创建一个新的 班级 称为 LazyPromise 扩展内置 Promise 构造函数。扩展 Promise 并不是绝对必要的,但它使它看起来更类似于 Promise 使用 instanceof.

class LazyPromise extends Promise {
  /** @param {ConstructorParameters<PromiseConstructor>[0]} executor */
  constructor(executor) {
    super(executor);
    if (typeof executor !== 'function') {
      throw new TypeError(`LazyPromise executor is not a function`);
    }
    this._executor = executor;
  }
  then() {
    this.promise = this.promise || new Promise(this._executor);
    return this.promise.then.apply(this.promise, arguments);
  }
}

要关注的部分是 then() 方法。它劫持了标准的默认行为, Promise 等待 .then() 方法执行后再创建真正的 Promise.

这避免了在您实际调用它之前实例化异步功能。无论您显式调用 .then() 还是使用 它,它都有效await

现在,让我们看看如果我们将 Promise 原始 sleep 函数中的 替换为 a 会发生什么LazyPromise。再一次,我们将结果分配给一个 nap 变量。

function sleep(ms, value) {
  return new LazyPromise((resolve) => {
    setTimeout(() => resolve(value), ms);
  });
}

const nap = sleep(1000, 'Yawn & stretch')

然后我们花时间输入该 await nap 行并执行它。

在这里插入图片描述

在 JavaScript 控制台中输入“await nap”并在一秒钟的延迟后看到响应“‘Yawn & stretch’”

Promise 这一次,无论变量创建后经过了多少时间,我们都会在解析前看到一秒的延迟 。

(请注意,此实现仅创建 Promise 一次新的并在后续调用中引用它。因此,如果我们 await 再次使用它,它将像任何正常情况一样立即解析 Promise

当然,这是一个您可能不会在生产代码中找到的微不足道的示例,但是有许多项目使用类似惰性求值 Promise的对象。可能最常见的示例是数据库 ORM和查询构建器,例如 Knex.js 或者 棱镜.

考虑下面的伪代码。它的灵感来自其中一些查询构建器:

const query = db('user')
  .select('name')
  .limit(10)

const users = await query

我们创建一个数据库查询,该查询转到该 "user" 表并选择前十个条目并返回它们的名称。从理论上讲,这对于常规 Promise.

但是如果我们想根据查询字符串参数等特定条件修改查询怎么办?能够在最终等待 Promise.

const query = db('user')
  .select('name')
  .limit(10)

if (orderBy) {
  query.orderBy(orderBy)
}
if (limit) {
  query.limit(limit)
}
if (id) {
  query.where({ id: id })
}

const users = await query

如果原始数据库查询是标准的 Promise,它会在我们分配变量后立即实例化查询,我们以后将无法修改它。

通过惰性求值,我们可以编写这样更容易理解的代码,改善开发人员体验,并且只在需要时执行一次查询。

这是惰性评估很棒的一个例子。它也可能对构建、修改和编排 HTTP 请求等有用。

Lazy Promise对于正确的用例来说非常酷,但这并不是说它们应该替换每个 Promise. 在某些情况下,尽快实例化并尽快准备好响应是有益的。

这是另一种“视情况而定”的情况。但下次有人要你做一个 Promise时,考虑偷懒吧 (͡° ͜ʖ ͡°)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值