代码之家  ›  专栏  ›  技术社区  ›  Regular Jo

离开jQuery,编写了一个简单的ajax函数,但是链式方法不会等待

  •  0
  • Regular Jo  · 技术社区  · 5 年前

    更新:添加了一个更简单的jsfiddle演示, https://jsfiddle.net/47sfj3Lv/3/ .

    用更少的代码重现这个问题,我正试图从jQuery中摆脱出来。

    我的一些用于填充某些表的代码有这样的代码

    var hb = new hbLister(url: '#attributes.listURL#')
      .getData(#url.page#, #url.per#)
      .search(searchColumn, searchParam)
      .render();
    
    • hbLister 会初始化一些东西
    • getData 将执行$.ajax调用
    • search console.log('filtering data') 并对javascript对象应用搜索条件
    • render

    重要的是, 在ajax调用之前不会启动 获取数据

    现在我有了这个ajax构造函数。我已经尽可能地简化了这段代码。

    let ajax = function (options, hooks, headers) {
      let that = this;
    
      // enforce parameter types
      // copy parameters to this.base
      this.base = { options: options, hooks: hooks, headers: headers }
    
      return function (url, options, data, hooks, headers) {
        // enforce variable types
        // merge options and hooks with their base.counterparts
    
        headers = new Headers(Object.assign({}, that.base.headers, headers));
        options.headers = headers;
    
        return fetch(url, options)
          .then(response => {
            return response.json().then(json => {
              console.log('processing');
              if (response.ok) {
                // it's omitted here but the Base functions are defined
                // in the constructor parameters
                hooks.successBase(json, response.status, response.headers);
                hooks.success(response.json, response.status, response.headers)
              } else {
                hooks.failureBase(json, response.status, response.headers);
                hooks.failure(response.json, response.status, response.headers)
              }
            })
          });
      }
    }
    

    我可以说

    let tableLister = new ajax()
    

    然后 可以打电话

    tableLister = tableLister(hb.url, // url
      { type: "GET" }, // options
      config.data, // data
      { success: successFn } // hooks, success being the callback
    )
    

    jQuery调用会正确地给我 processing filtering data .

    这个函数给了我 处理 因为我好像拿不到锁链( .search(...).render() )等待ajax调用完成。

    下面是一个关于jsFiddle的独立示例, https://jsfiddle.net/47sfj3Lv/3/

    我确信答案是异步的,等待着,但我还没能让它工作。

    这是我尝试过的一个例子

      return await (async function () {
        console.log('fetching')
        let fetcher = await fetch(url, options);
    
        console.log('getting json');
        return await fetcher.json().then((json) => {
          console.log('have json, doing hooks');
          if (fetcher.ok) {
            let argXHR = { json: json}
            hooks.successBase(argXHR, hooks.params);
            hooks.success.forEach(v => v(argXHR, hooks.params));
            hooks.afterSend(argXHR, hooks.params);
          } else {
            let argXHR = { json: json,}
            hooks.failureBase(argXHR, hooks.params);
            hooks.failure.forEach(v => v(argXHR, hooks.params));
            hooks.afterError(argXHR, hooks.params);
          }
          console.log('finished hooks')
        })
      }())
    

    不管我怎么做,在等待结束之前,锁链还在继续。。


    .getData().search(...).render() )可以这样做,因为这不允许ajax函数在请求完成和回调执行之前返回**我还是更愿意 .fetch()

      let xhr = new XMLHttpRequest()
    
      let urlParams = [];
      Object.keys(data).forEach((v) => urlParams.push(v + '=' + encodeURIComponent(data[v])));
      urlParams = urlParams.join('&')
    
      xhr.open(options.method, options.url, false);
      xhr.onreadystatechange = function(state) {
        if (this.readyState == 4) {
          let json = JSON.parse(xhr.responseText)
          hooks.successBase(json, xhr.status, xhr.getResponseHeader);
          hooks.success.forEach(v => v(json, xhr.status, xhr.getResponseHeader));
        }
      }
      xhr.onerror = function() {
        let json = JSON.parse(xhr.responseText)
        hooks.failureBase(json, xhr.status, xhr.getResponseHeader);
        hooks.failure.forEach(v => v(json, xhr.status, xhr.getResponseHeader));
      }
      for (h in headers) {
        xhr.setRequestHeader(h, headers[h])
      }
      xhr.send(urlParams)
    
    0 回复  |  直到 5 年前
        1
  •  0
  •   Regular Jo    5 年前

    似乎异步方法会破坏方法链,这是没有办法的。由于fetch是异步的,所以必须使用await,并且为了使用await,调用方法必须声明为async。因此方法链将被打破。

    调用方法链的方式必须更改。

    在我的专栏里,我把 https://jsfiddle.net/47sfj3Lv/3/ 作为同一问题的更简单版本。出于安全原因,StackOverflow的fiddle有效地阻止了fetch,所以我需要使用JSFiddle进行演示。

    Here's a working version then 以及它的工作原理 slightly shorter version ,因为await显然可以和fetch一起指定。

    let obj = {};
    
    // methods with fetch ideally should be specified async.
    // async calls will automatically return a promise
    obj.start = async () => {
      console.log('start begins')
      let retText = "",
        fetcher = fetch('/', {}).then(response => response.text())
        .then(text => {
          console.log('fetch\'s last then')
          retText = text
        })
      // fetcher has just been declared. It hasn't done anything yet.
      console.log('fetch requested, returned length:', retText.length)
    
      // this makes the fetcher and sequential then's happen
      await fetcher;
      console.log('await let fetch finish, returned length:', retText.length)
    
      // Async functions will return a promise, but the return specified here
      // will be passed to the next 'then'
      return obj
    }
    obj.middle = () => {
      console.log('middle called')
      // Because this is not declared async, it behaves normally
      return obj;
    }
    obj.end = () => {
      console.log('end called')
      // Because this is not declared async, it behaves normally
    }
    
    console.log('Method 1: ', obj.start()
      // Because start is Async, it returns obj wrapped in a promise.
      // As with any function, step may be named Anything you like
      .then((step) => step.middle())
      // Middle is not Async, but since it's wrapped in then
      // it returns a promise
      .then((step) => step.end())
    )
    
    // This is just wrapped in a timer so the two logs don't intermix with each other
    
    // This is definitely the preferred method. Non-async chain-methods that return 
    // a reference to their object, do not need to wrapped in then().
    setTimeout(function() {
      console.log('------------------------')
      console.log('Method 2: ', obj.start()
        // Because start is Async, it returns obj wrapped in a promise.
        // As with any function, step may be named Anything you like
        .then((step) => step.middle().end())
      )
    }, 3000)