为什么?
了解 JavaScript Promises 如何在后台异步运行回调。
让我们用 JavaScript 创建我们自己的 Promise!我们将遵循 Promise/A 规范,该规范概述了 Promise 如何处理异步操作、解析、拒绝以及确保可预测的链接和错误处理。
为了简单起见,我们将重点关注 Promises/A 规范中 ✅ 标记的关键规则。这不是一个完整的实现,而是一个简化版本。这是我们将构建的:
1. 术语
1.1 'promise' 是一个带有 then 方法的对象或函数,其行为符合此规范。
1.2 thenable'是一个定义了then方法的对象或函数。
1.3 'value' 是任何合法的 JavaScript 值(包括未定义的、thenable 或一个promise)。
1.4 “异常”是使用 throw 语句抛出的值。
1.5 'reason' 是一个值,表示承诺被拒绝的原因。
2. 要求
2.1 承诺状态
承诺必须处于以下三种状态之一:待定、已履行或已拒绝。
2.1.1。待处理时,承诺:✅
⟶ 可能会转换为已完成或已拒绝状态。
2.1.2。兑现后,承诺:✅
⟶不得转换到任何其他状态。
⟶ 必须有一个值,且不得更改。
2.1.3。当被拒绝时,一个承诺:✅
⟶不得转换到任何其他状态。
⟶必须有一个理由,并且这个理由不能改变。
2.2 then方法
promise 必须提供 then 方法来访问其当前或最终的值或原因。
promise 的 then 方法接受两个参数:
promise.then(onFulfilled, onRejected);
2.2.1。 onFulfilled 和 onRejected 都是可选参数:✅
⟶ 如果 onFulfilled 不是函数,则必须忽略它。
⟶ 如果 onRejected 不是函数,则必须忽略它。
2.2.2。如果 onFulfilled 是一个函数: ✅
⟶ 它必须在 Promise 完成后调用,并以 Promise 的值作为第一个参数。
⟶ 在承诺完成之前不得调用它。
⟶ 不得多次调用。
2.2.3。如果 onRejected 是一个函数,✅
⟶ 它必须在 Promise 被拒绝后调用,并以 Promise 的原因作为第一个参数。
⟶ 在承诺被拒绝之前不得调用它。
⟶ 不得多次调用。
2.2.4。在执行上下文堆栈仅包含平台代码之前,不得调用 onFulfilled 或 onRejected。 ✅
2.2.5。 onFulfilled 和 onRejected 必须作为函数调用(即没有 this 值)。 ✅
2.2.6。 then 可能会针对同一个 Promise 被多次调用。 ✅
⟶ 如果/当 Promise 被履行时,所有相应的 onFulfilled 回调必须按照它们最初调用 then 的顺序执行。
⟶ 如果/当 Promise 被拒绝时,所有相应的 onRejected 回调必须按照其原始调用 then 的顺序执行。
2.2.7。那么必须返回一个承诺。 ✅
promise.then(onFulfilled, onRejected);
⟶ 如果 onFulfilled 或 onRejected 返回值 x,则运行 Promise 解析过程 [[Resolve]](promise2, x)。 ❌
⟶ 如果 onFulfilled 或 onRejected 抛出异常 e,则 Promise2 必须以 e 作为原因被拒绝。 ❌
⟶ 如果 onFulfilled 不是函数并且 Promise1 已实现,则 Promise2 必须以与 Promise1 相同的值来实现。 ❌
⟶ 如果 onRejected 不是函数并且 Promise1 被拒绝,则 Promise2 也必须以与 Promise1 相同的原因被拒绝。 ❌
执行
JavaScript Promise 采用执行器函数作为参数,该函数在 Promise 创建时立即调用:
promise2 = promise1.then(onFulfilled, onRejected);
new Promise(excecutor);
核心 Promises/A 规范不涉及如何创建、履行或拒绝 Promise。由你决定。但是您为 Promise 构造提供的实现必须与 JavaScript 中的异步 API 兼容。这是我们 Promise 类的初稿:
const promise = new Promise((resolve, reject) => { // Runs some async or sync tasks });
规则 2.1(Promise 状态)规定,Promise 必须处于以下三种状态之一:待处理、已履行或已拒绝。它还解释了每个状态中发生的情况。
当履行或拒绝时,承诺不得转变为任何其他状态。因此,我们需要在进行任何转换之前确保 Promise 处于待处理状态:
class YourPromise { constructor(executor) { this.state = 'pending'; this.value = undefined; this.reason = undefined; const resolve = value => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; } }; const reject = reason => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; } }; try { executor(resolve, reject); // The executor function being called immediately } catch (error) { reject(error); } } }
我们已经知道 Promise 的初始状态是待处理的,并且我们确保它保持这种状态,直到明确履行或拒绝:
const resolve = value => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; } }; const reject = reason => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; } };
由于执行器函数在 Promise 实例化后立即被调用,因此我们在构造函数方法中调用它:
this.state = 'pending';
我们的 YourPromise 类的初稿已在此处完成。
Promise/A 规范主要关注定义可互操作的 then() 方法。这个方法让我们可以访问promise的当前或最终值或原因。让我们深入探讨一下。
规则 2.2(Then 方法)规定 Promise 必须有一个 then() 方法,该方法接受两个参数:
try { executor(resolve, reject); } catch (error) { reject(error); }
onFulfilled 和 onRejected 都必须在 Promise 完成或拒绝后调用,如果它们是函数,则传递 Promise 的值或原因作为它们的第一个参数:
class YourPromise { constructor(executor) { // Implementation } then(onFulfilled, onRejected) { // Implementation } }
此外,在承诺履行或拒绝之前,不得调用它们,也不得调用超过一次。 onFulfilled 和 onRejected 都是可选的,如果它们不是函数,则应忽略它们。
如果你看一下规则 2.2、2.2.6 和 2.2.7,你会发现一个 Promise 必须有一个 then() 方法,then() 方法可以被多次调用,并且它必须返回一个承诺:
promise.then(onFulfilled, onRejected);
为了简单起见,我们不会处理单独的类或函数。我们将返回一个 Promise 对象,并传递一个执行器函数:
promise2 = promise1.then(onFulfilled, onRejected);
在执行器函数中,如果 Promise 被履行,我们会调用 onFulfilled 回调并使用 Promise 的值来解析它。同样,如果 Promise 被拒绝,我们会调用 onRejected 回调并以 Promise 的原因拒绝它。
下一个问题是,如果 Promise 仍处于待处理状态,如何处理 onFulfilled 和 onRejected 回调?我们将它们排队以便稍后调用,如下所示:
new Promise(excecutor);
我们完成了。这是 Promise 类的第二稿,包括 then() 方法:
const promise = new Promise((resolve, reject) => { // Runs some async or sync tasks });
这里,我们引入两个字段:onFulfilledCallbacks 和 onRejectedCallbacks 作为保存回调的队列。当 Promise 未决时,这些队列通过 then() 调用填充回调,并且当 Promise 被履行或拒绝时调用它们。
继续测试你的 Promise 类:
class YourPromise { constructor(executor) { this.state = 'pending'; this.value = undefined; this.reason = undefined; const resolve = value => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; } }; const reject = reason => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; } }; try { executor(resolve, reject); // The executor function being called immediately } catch (error) { reject(error); } } }
它应该输出:
const resolve = value => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; } }; const reject = reason => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; } };
另一方面,如果您运行以下测试:
this.state = 'pending';
你会得到:
try { executor(resolve, reject); } catch (error) { reject(error); }
代替:
class YourPromise { constructor(executor) { // Implementation } then(onFulfilled, onRejected) { // Implementation } }
为什么?问题在于,当调用 then() 时 YourPromise 实例已被解析或拒绝时,then() 方法如何处理回调。具体来说,当 Promise 状态不是待处理时,then() 方法不会正确地将回调的执行推迟到下一个微任务队列。这会导致同步执行。在我们的示例测试中:
⟶ 承诺立即解决,值为“立即解决”。
⟶ 当调用promise.then()时,状态已经完成,所以onFulfilled回调会直接执行,不会推迟到下一个微任务队列。
这里规则 2.2.4 发挥作用。此规则确保 then() 回调(onFulfilled 或 onRejected)异步执行,即使 Promise 已解决或拒绝。这意味着回调不得运行,直到当前执行堆栈完全清除并且只有平台代码(如事件循环或微任务队列)正在运行。
为什么这条规则很重要?
这条规则是 Promise/A 规范中最重要的规则之一。因为它确保:
⟶ 即使 Promise 立即得到解决,其 then() 回调也不会执行,直到事件循环的下一个标记。
⟶ 此行为与 JavaScript 中其他异步 API(例如 setTimeout 或 process.nextTick)的行为一致。
我们怎样才能做到这一点?
这可以通过宏任务机制(如setTimeout或setImmediate)或微任务机制(如queueMicrotask或process.nextTick)来实现。因为微任务或宏任务或类似机制中的回调将在当前 JavaScript 执行上下文完成后执行。
为了解决上述问题,我们需要确保即使状态已经完成或拒绝,相应的回调(onFulfilled或onRejected)也使用queueMicrotask异步执行。这是更正后的实现:
promise.then(onFulfilled, onRejected);
再次运行前面的示例测试代码。您应该得到以下输出:
promise2 = promise1.then(onFulfilled, onRejected);
就是这样。
现在,您应该清楚地了解 then() 的回调如何延迟并在下一个微任务队列中执行,从而实现异步行为。牢牢掌握这个概念对于在 JavaScript 中编写有效的异步代码至关重要。
下一步是什么?由于本文没有涵盖完整的 Promises/A 规范,您可以尝试实现其余部分以获得更深入的理解。
既然你已经读到这里了,希望你喜欢阅读这篇文章!请分享文章。
关注我:
LinkedIn、Medium 和 Github
以上是在 JavaScript 中创建您自己的 Promise的详细内容。更多信息请关注PHP中文网其他相关文章!

引言我知道你可能会觉得奇怪,JavaScript、C 和浏览器之间到底有什么关系?它们之间看似毫无关联,但实际上,它们在现代网络开发中扮演着非常重要的角色。今天我们就来深入探讨一下这三者之间的紧密联系。通过这篇文章,你将了解到JavaScript如何在浏览器中运行,C 在浏览器引擎中的作用,以及它们如何共同推动网页的渲染和交互。JavaScript与浏览器的关系我们都知道,JavaScript是前端开发的核心语言,它直接在浏览器中运行,让网页变得生动有趣。你是否曾经想过,为什么JavaScr

Node.js擅长于高效I/O,这在很大程度上要归功于流。 流媒体汇总处理数据,避免内存过载 - 大型文件,网络任务和实时应用程序的理想。将流与打字稿的类型安全结合起来创建POWE

Python和JavaScript在性能和效率方面的差异主要体现在:1)Python作为解释型语言,运行速度较慢,但开发效率高,适合快速原型开发;2)JavaScript在浏览器中受限于单线程,但在Node.js中可利用多线程和异步I/O提升性能,两者在实际项目中各有优势。

JavaScript起源于1995年,由布兰登·艾克创造,实现语言为C语言。1.C语言为JavaScript提供了高性能和系统级编程能力。2.JavaScript的内存管理和性能优化依赖于C语言。3.C语言的跨平台特性帮助JavaScript在不同操作系统上高效运行。

JavaScript在浏览器和Node.js环境中运行,依赖JavaScript引擎解析和执行代码。1)解析阶段生成抽象语法树(AST);2)编译阶段将AST转换为字节码或机器码;3)执行阶段执行编译后的代码。

Python和JavaScript的未来趋势包括:1.Python将巩固在科学计算和AI领域的地位,2.JavaScript将推动Web技术发展,3.跨平台开发将成为热门,4.性能优化将是重点。两者都将继续在各自领域扩展应用场景,并在性能上有更多突破。

Python和JavaScript在开发环境上的选择都很重要。1)Python的开发环境包括PyCharm、JupyterNotebook和Anaconda,适合数据科学和快速原型开发。2)JavaScript的开发环境包括Node.js、VSCode和Webpack,适用于前端和后端开发。根据项目需求选择合适的工具可以提高开发效率和项目成功率。

是的,JavaScript的引擎核心是用C语言编写的。1)C语言提供了高效性能和底层控制,适合JavaScript引擎的开发。2)以V8引擎为例,其核心用C 编写,结合了C的效率和面向对象特性。3)JavaScript引擎的工作原理包括解析、编译和执行,C语言在这些过程中发挥关键作用。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

Dreamweaver CS6
视觉化网页开发工具

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

适用于 Eclipse 的 SAP NetWeaver 服务器适配器
将Eclipse与SAP NetWeaver应用服务器集成。

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

MinGW - 适用于 Windows 的极简 GNU
这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。