• 使用 Promise 来改写 JavaScript 的加载逻辑


    传统的基于 callback 的实现方式:

    function loadScript(src, callback) {
      let script = document.createElement('script');
      script.src = src;
    
      script.onload = () => callback(null, script);
      script.onerror = () => callback(new Error(`Script load error for ${src}`));
    
      document.head.append(script);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果使用 promise,则避免了回调函数的使用。

    新函数 loadScript 不需要回调。 相反,它将创建并返回一个 Promise 对象,该对象在加载完成时解析。 外部代码可以使用 .then 向其添加处理程序(订阅函数):

    function loadScript(src) {
      return new Promise(function(resolve, reject) {
        let script = document.createElement('script');
        script.src = src;
    
        script.onload = () => resolve(script);
        script.onerror = () => reject(new Error(`Script load error for ${src}`));
    
        document.head.append(script);
      });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    如何使用这个 loadScript

    let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js");
    
    promise.then(
      script => alert(`${script.src} is loaded!`),
      error => alert(`Error: ${error.message}`)
    );
    
    promise.then(script => alert('Another handler...'));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    使用 Promise 编写代码,允许我们按照自然顺序做事。 首先,我们运行 loadScript(script),然后我们使用类似自然语言的语法 then,编写如何处理结果。

    反观 callback 解决方案,调用 loadScript(script, callback) 时,我们必须有一个回调函数可供使用。 换句话说,我们必须在调用 loadScript 之前知道如何处理结果。

    我们可以根据需要多次调用 .then 。 每次,我们都会在“订阅列表”中添加一个新的“粉丝”,一个新的订阅功能。而 Callback 的解决方案,只支持单个 Callback.

    我们如果多次在 executor 里调用 resolve,会发生什么情形?

    比如考虑上述代码:

    let promise = new Promise(function(resolve, reject) {
      resolve(1);
    
      setTimeout(() => resolve(2), 1000);
    });
    
    promise.then(alert);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    答案是 1.

    第二次调用resolve被忽略,因为只考虑第一次reject/resolve调用。 进一步的调用将被忽略。

    promise 有很多妙用,比如用来实现 delay 函数。

    function delay(ms) {
      return new Promise(resolve => setTimeout(resolve, ms));
    }
    
    delay(3000).then(() => alert('runs after 3 seconds'));
    
    • 1
    • 2
    • 3
    • 4
    • 5

    delay 的 executor 里,三秒之后再调用 resolve,因而实现了 delay 效果。

    请注意,在这个任务中,resolve 的写法,含义是 resolve 被调用,没有参数。 我们不会从延迟中返回任何值,只是确保延迟。

    下面是用 promise 实现的一个特效:

    DOCTYPE html>
    <html>
    
    <head>
      <meta charset="utf-8">
      <style>
        .message-ball {
          font-size: 20px;
          line-height: 200px;
          text-align: center;
        }
        .circle {
          transition-property: width, height;
          transition-duration: 2s;
          position: fixed;
          transform: translateX(-50%) translateY(-50%);
          background-color: red;
          border-radius: 50%;
        }
      style>
    head>
    
    <body>
    
      <button onclick="go()">Click mebutton>
    
      <script>
    
      function go() {
        showCircle(150, 150, 100).then(div => {
          div.classList.add('message-ball');
          div.append("Hello, world!");
        });
      }
    
      function showCircle(cx, cy, radius) {
        let div = document.createElement('div');
        div.style.width = 0;
        div.style.height = 0;
        div.style.left = cx + 'px';
        div.style.top = cy + 'px';
        div.className = 'circle';
        document.body.append(div);
    
        return new Promise(resolve => {
          setTimeout(() => {
            div.style.width = radius * 2 + 'px';
            div.style.height = radius * 2 + 'px';
    
            div.addEventListener('transitionend', function handler() {
              div.removeEventListener('transitionend', handler);
              resolve(div);
            });
          }, 0);
        })
      }
      script>
    
    
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    点击 Click me 按钮后,会出现一个圆形。

  • 相关阅读:
    网络原理 | 传输层重点协议之TCP协议(TCP连接的三次握手与四次挥手、TCP的安全机制与效率机制)
    VoLTE基础自学系列 | eSRVCC稳态呼叫切换流程
    Unity之Hololens开发如何实现UI交互
    为全志T507-H开发板配置Samba服务,高效实现跨系统的文件共享
    【Linux系统】第一篇:基础指令篇
    IPSec NAT穿越原理
    C++独立编译和命名空间
    监控画面时间同步校准
    SpringBoot自动配置原理解析
    C++面向对象OOP-友元与运算符重载
  • 原文地址:https://blog.csdn.net/i042416/article/details/126299432