最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 回调地狱的几种简单的解决方式

    正文概述 掘金(聒噪的观月叶蝉)   2021-03-08   576

    前言

    本文以学习为目的分享回调地狱的几种简单的解决方式,处理较为粗糙

    回调地狱

    首先我们来看一个例子,这个例子不断地嵌套,不断地缩进,使代码难以阅读、难以维护。

    const xhr1 = new XMLHttpRequest();
    xhr1.open("GET", 'http://127.0.0.1:5500/index.html');
    xhr1.send();
    xhr1.onreadystatechange = function () {
        if (xhr1.readyState === 4) {
            if (xhr1.status >= 200 && xhr1.status < 300) {
                console.log(`success1: ${xhr1.response}`)
    
                const xhr2 = new XMLHttpRequest();
                xhr2.open("GET", 'http://127.0.0.1:5500/index2.html');
                xhr2.send();
                xhr2.onreadystatechange = function () {
                    if (xhr2.readyState === 4) {
                        if (xhr2.status >= 200 && xhr2.status < 300) {
                            console.log(`success2: ${xhr2.response}`)
    
                            const xhr3 = new XMLHttpRequest();
                            xhr3.open("GET", 'http://127.0.0.1:5500/index3.html');
                            xhr3.send();
                            xhr3.onreadystatechange = function () {
                                if (xhr3.readyState === 4) {
                                    if (xhr3.status >= 200 && xhr3.status < 300) {
                                        console.log(`success3: ${xhr3.response}`)
                                    } else {
                                        console.log(`error3: ${xhr3.status}`);
                                    }
                                }
                            };
                        } else {
                            console.log(`error2: ${xhr2.status}`);
                        }
                    }
                };
            } else {
                console.log(`error1: ${xhr1.status}`);
            }
        }
    };
    

    通过抽取函数解决回调问题

    我们可以抽取函数来解决回调问题,我们将上面的代码进行优化

    function request1 () {
      const xhr = new XMLHttpRequest();
      xhr.open("GET", 'http://127.0.0.1:5500/index.html');
      xhr.send();
      xhr.onreadystatechange = function () {
          if (xhr.readyState === 4) {
              if (xhr.status >= 200 && xhr.status < 300) {
                  console.log(`success1: ${xhr.response}`)
    
                  request2(xhr.response);
              } else {
                  console.log(`error1: ${xhr.status}`);
              }
          }
      };
    }
    
    function request2 (response) {
      const xhr = new XMLHttpRequest();
      xhr.open("GET", 'http://127.0.0.1:5500/index2.html');
      xhr.send();
      xhr.onreadystatechange = function () {
          if (xhr.readyState === 4) {
              if (xhr.status >= 200 && xhr.status < 300) {
                  console.log(`success1: ${xhr.response}`)
    
                  request3(xhr.response);
              } else {
                  console.log(`error1: ${xhr.status}`);
              }
          }
      };
    }
    
    function request3 (response) {
      const xhr = new XMLHttpRequest();
      xhr.open("GET", 'http://127.0.0.1:5500/index3.html');
      xhr.send();
      xhr.onreadystatechange = function () {
          if (xhr.readyState === 4) {
              if (xhr.status >= 200 && xhr.status < 300) {
                  console.log(`success1: ${xhr.response}`)
              } else {
                  console.log(`error1: ${xhr.status}`);
              }
          }
      };
    }
    
    request1();
    

    通过 Promise 解决回调地狱

    基础用法

    在 MDN 中定义:Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。

    Promise a+规范及其翻译版

    简单来讲就是我们通过 Promise 类实例化 promise 对象可进行异步操作,其共有三个状态,初始为 PENDING , 成功则通过 resolve 将其更改为 RESOLVED ,并自动调用 then ,失败则通过 reject 更改为 REJECTED ,并自动调用 catch ,状态一经改变则不会再更改

    注意,在实例化 promise 时的回调函数本身是同步的,只有 thencatch 中的逻辑是异步的

    例子

    const p = new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      // 我使用了 Live Server 插件,这里请求的本文档
      xhr.open("GET", "http://127.0.0.1:5500/index.html");
      xhr.send();
      xhr.onreadystatechange = function () {
          if (xhr.readyState === 4) {
              if (xhr.status >= 200 && xhr.status < 300) {
                  resolve(xhr.response);
              } else {
                  reject(xhr.status);
              }
          }
      };
    });
    
    p.then((res) => {
      console.log('then: ' + res);
    }).catch((err) => {
      console.log('catch: ' + err);
    });
    

    链式调用

    因为 thencatch 返回的是 promise 对象,所以我们可以进行链式调用,这样我们就轻松解决了回调地狱的问题。

    // 重复代码过多,这里写了个函数来生成 Promise 对象
    function createPromise(url) {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", url);
        xhr.send();
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    console.log(`resolve:${url}`)
                    resolve(xhr.response);
                } else {
                    console.log(`reject:${url}`)
                    reject(xhr.status);
                }
            }
        };
      }) 
    }
    
    const p1 = createPromise("http://127.0.0.1:5500/index.html")
    const p2 = createPromise("http://127.0.0.1:5500/index2.html")
    const p3 = createPromise("http://127.0.0.1:5500/index3.html")
    
    const await1 = p1.then((res) => {
      console.log('then1: ' + res);
      return p2;
    }).then((res) => {
      console.log('then2: ' + res);
      return p3;
    }).then((res) => {
      console.log('then3: ' + res);
    }).catch((err) => {
      console.log('catch: ' + err);
    })
    

    通过 syncawait 解决回调地狱

    asyncawaitpromise 的语法糖

    async 用于声明一个 function 是异步的,而 async 函数的返回值是一个 promise 对象

    await 必须在 async 函数中使用,会阻塞后面的异步语句,直到返回表达式结果,而后面如果是 promise 则会等待其变为 resolved 状态,并以其返回值作为运算结果。

    通过 asyncawait 我们将异步变成了同步,但是同时也有一个问题, async 的返回值是一个 promise 对象,所以我们获取 async 函数的返回值时也需要使用 await 运算符,这就造成了 async 的地狱。

    async function asyncFun1 () {
      return 1;
    }
    console.log(asyncFun1()) // Promise {<fulfilled>: 1}
    
    async function asyncFun2 () {
      console.log('asyncFun2: ' + 1)
      try {
          const awaitResult1 = await p1;
          console.log('asyncFun2: ' + awaitResult1)
      } catch (e) {
          console.log('asyncFun2: ' + e);
      }
      // 字符串相当于状态为 RESOLVED 的 promise 对象
      const awaitResult2 = await 'Hello World';
      console.log('asyncFun2: ' + awaitResult2)
      console.log('asyncFun2: ' + 2)
      return awaitResult2;
    }
    
    async function callAsyncFun () {
      const result = await asyncFun2();
      console.log('callAsyncFun: ' + result);
    }
    
    callAsyncFun()
    

    起源地下载网 » 回调地狱的几种简单的解决方式

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元