马黑黑 发表于 2026-3-17 18:46

初识JS Promise 对象

<style>
        .artBox { font: normal 18px/1.5 sans-serif; overflow: auto; position: relative; }
        .artBox p { margin: 10px 0; }
        .artBox h1, .artBox h2 { margin: 8px 0; }
        .artBox code, .artBox pre { background: rgba(0,128,128,.25); padding: 2px 6px; tab-size: 4; }
        .artBox pre { padding: 10px 20px; white-space: pre-wrap; word-wrap: break-word; }
        .artBox pre code { padding: 0; background: none; }
        .artBox blockquote { margin: 10px 20px; padding: 2px 15px; border-left: 3px solid skyblue; background: rgba(240,248,255,.65); }
        .artBox table { border-collapse: collapse; white-space: pre-wrap; box-sizing: border-box; }
        .artBox th, .artBox td { padding: 8px 10px; border: 1px solid #999; }
        .artBox th { text-align: center; background: #eee; }
</style>

<div class="artBox">
    <p>在 Discuz! 论坛中使用JS最简洁的方式是ES导入,美中不足的是它无法与评分刷新机制完美适配,因此可以退而求其次采用回调函数加载JS资源,它解决了前者的痛点问题。</p>
    <p>回调函数理论上可以加载多个JS资源,问题是容易陷入“厄运金字塔”困境,俗称“回调地狱”。加载一两个JS文档,回调函数是完全胜任的,多了令人头疼。仅从结构上看,考虑如下代码:</p>
    <pre><code>// 假设已经有了一个 loadJs() 函数
loadJs(f1) {
    loadJs(f2) {
      loadJs(f3) {
            loadJs(f4) {
                // ... 这里是业务核心代码
            }
      }
    }
}</code></pre>
    <p>四个JS文件都需要全部加载完毕,业务核心代码才能出场,它处在躺平金字塔的最右侧抑或地狱的最底层。结构层面的代码无限缩进并非问题所在,致命的应该是业务逻辑问题:每一个被成功加载后可能都有各自的业务逻辑,业务代码需要分散穿插其中,分散且凌乱;其中的一个资源加载失败,局面就更不可控。</p>
    <p>因此,Promise 对象应运而生,它会使得代码结构“扁平化”、代码逻辑清晰化。试看:</p>
    <pre><code>// 假设已经有了一个返回 promise 的 loadJs() 函数
loadJs(f1)
    .then(()=&gt; loadJs(f2))
    .then(() =&gt; loadJs(f3))
    .then(loadJs(f4))
    .then(()=&gt; {
      // ... 这里是业务逻辑
    });</code></pre>
    <p>每一个成功加载的JS如果有对应于它的业务逻辑,均可在 .then 后面加入代码块,如果没有就像上述代码那样顺延下去,直至最后一个资源加载完毕,业务核心代码最后展开。向下扩展的代码结构使得代码的结构自身“扁平化”、更易于阅读,代码逻辑也因此更明晰。</p>
    <p>那么,如何使用 Promise 对象编写加载JS资源的函数呢?请看:</p>
    <pre><code>function loadJs(src) {
    return new Promise(function(resolve, reject) {
      let script = document.createElement('script');
      script.src = src;
      script.onload = () =&gt; resolve(script);
      script.onerror = () =&gt; reject(new Error(`Script load error for ${src}`));
      document.head.append(script);
    });
}</code></pre>
    <blockquote>
      <p>Promise 是一个 ECMAScript 6 提供的类,目的是更加优雅地书写复杂的异步任务。</p>
      <p>Promise 是 JavaScript 中用于处理异步操作的对象,它代表一个异步操作的最终完成(或失败)及其结果值。</p>
      <p>Promise 有三种状态:</p>
      <p>&#128160; pending:初始状态,既不是成功,也不是失败状态</p>
      <p>&#128160; fulfilled:意味着操作成功完成</p>
      <p>&#128160; rejected:意味着操作失败</p>
      <p>简单来说,Promise 是一个“承诺”,表示将来某个时间点会返回一个结果:可能是成功的结果,也可能是失败的原因。必须会有返回,所以是“真诚承诺”。</p>
    </blockquote>
    <p>可以像前面那样直接调用上述函数,也可以这样:</p>
    <pre><code>var promise = loadJs('./yourfile.js');
promise.then(
    script =&gt; tzInit(), // JS文件加载后执行业务函数
    err =&gt; console.log(`Error: ${err.message}`) // 加载失败返回错误信息
);</code></pre>
    <p>promise 是 Promise 对象的一个实例,来自于 loadJs 函数。Promise对象还允许使用 try/catch/finally 结构来实现更加复杂、严谨的业务逻辑。</p>
    <p>【题外话】</p>
    <blockquote>
      <p>Promise 其实是一个古老的概念,于1976年提出,它与未来有关(承诺都是针对未来的时间点,虽然未来已来)。JS于2015年在其推出的 ES6 中集成了此对象,它解决了回调可能出现的平躺厄运,也能以无限的链式调用制造新的then地狱,因此次年,它妈妈又生了一窝代号为 ES7 的孩纸,里面的小老弟 async/await 才是异步终极的解决方案。</p>
    </blockquote>
</div>

马黑黑 发表于 2026-3-17 20:14

Promise 音频处理案例(2024年2月下旬发的帖子):


    Promise处理音频自动播放实例 - 马黑黑教程专版 - 花潮论坛 - Powered by Discuz!

该案例就是一个经典的 Promise 应用实例,特别是错误处理做了两个分支:音频有效但不支持自动播放、音频无效。

audio 控件的 play 事件本身就是一个 Promise 对象(有人也称之为类),充分利用其返回值,可以从容处理各种可能的问题。

红影 发表于 2026-3-18 08:56

马黑黑 发表于 2026-3-17 20:14
Promise 音频处理案例(2024年2月下旬发的帖子):




原来黑黑那么早就已经介绍了Promise的使用了啊,对这东西太陌生,完全没记住{:4_173:}

红影 发表于 2026-3-18 09:00

Promise 使得代码逻辑清晰,更易于阅读和检查,真不错。
感谢黑黑,总是带来很多知识,虽然现在没记住,但先了解一下也很不错,以后用到肯定会记住的{:4_187:}

马黑黑 发表于 2026-3-18 09:25

红影 发表于 2026-3-18 08:56
原来黑黑那么早就已经介绍了Promise的使用了啊,对这东西太陌生,完全没记住

这个东东有点抽象

马黑黑 发表于 2026-3-18 09:26

红影 发表于 2026-3-18 09:00
Promise 使得代码逻辑清晰,更易于阅读和检查,真不错。
感谢黑黑,总是带来很多知识,虽然现在没记住,但 ...

真正使用到了并在实践中做尝试才会记住,否则就是简单了解一下便了

红影 发表于 2026-3-18 12:03

马黑黑 发表于 2026-3-18 09:25
这个东东有点抽象

非常实用呢。

红影 发表于 2026-3-18 12:04

马黑黑 发表于 2026-3-18 09:26
真正使用到了并在实践中做尝试才会记住,否则就是简单了解一下便了

现在的我也只能是了解一下,没本事使用呢{:4_173:}

马黑黑 发表于 2026-3-18 14:29

红影 发表于 2026-3-18 12:04
现在的我也只能是了解一下,没本事使用呢

别说你,就是经常使用JS工作得人,页未必百分百会用上 Promise

马黑黑 发表于 2026-3-18 14:30

红影 发表于 2026-3-18 12:03
非常实用呢。

那是相当实用的,不过如果回调可以解决问题,且不会陷入回调地狱,用回调更简单

杨帆 发表于 2026-3-18 22:04

谢谢老师经典讲授~不知为何,一看到这个Promise ,就联想到了玩频谱的痛,为何要有这样的策略呢?

“async/await 才是异步终极的解决方案。”async/await 是 Promise 的 “语法糖”,本质还是 Promise呀?

马黑黑 发表于 2026-3-19 20:15

杨帆 发表于 2026-3-18 22:04
谢谢老师经典讲授~不知为何,一看到这个Promise ,就联想到了玩频谱的痛,为何要有这样的策略呢?

“asy ...
关于音视频自动播放限制策略:

这是出于特定需求,核心是避免打扰,还有流量等等问题。这种理念,或许国人不太能接受,但应该能理解。

而基于其它层面,策略仍然是需要的。比如进程执行的异步需求:读取数据需要时间,但不能因为它而停下所有的其它进程,所以,当进程一需要进程二提供的数据,进程二很耗时,这时,就需要这个 Promise 对象,具体而言,进程一等待进程二的数据,期间进程三、四等等照常运行,进程二执行完毕可以提供数据了,进程一就在这个事件节点抓住自己的机会“插队”运行;又比如限制需求:由于安全等种种原因,浏览器总会对某些东东进行各种限制,这时,JS需要通过 Promise 对象(类)找到正在运行的进程是否被限制或放行,从而决定该如何处理。

策略不是想当然的,都是基于现实需要和现实状况而制定。

关于 async/await 是 promise 的语法糖:

这种说法只能说基本正确。本质上,async/await 也依赖 Promise 对象,但二者并不完全等同,否则制定者就是白痴。

“语法糖”的说法低估了 async/wait 所带来的工程价值。客观而言,async/await 不仅是语法上的简化,更是对异步编程模式的重大改进,显著提升了代码的可读性、可维护性和开发体验。因此,可以更全面地理解为:async/await 是基于 Promise 的、更高级的异步编程语法抽象。

杨帆 发表于 2026-3-19 21:27

本帖最后由 杨帆 于 2026-3-19 21:29 编辑

马黑黑 发表于 2026-3-19 20:15
关于音视频自动播放限制策略:

这是出于特定需求,核心是避免打扰,还有流量等等问题。这种理念,或许 ...
感谢老师全面、系统、专业的权威解答,有豁然开朗之感,对音视频自动播放限制策略的制定及async/await 与 Promise的关系有了进一步的理解,由衷感谢{:4_180:}

马黑黑 发表于 2026-3-19 22:01

杨帆 发表于 2026-3-19 21:27
感谢老师全面、系统、专业的权威解答,有豁然开朗之感,对音视频自动播放限制策略的制定及async/await 与 ...

{:4_191:}
页: [1]
查看完整版本: 初识JS Promise 对象