繁体   English   中英

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

[英]Leaving jQuery, wrote a simple ajax function, but chained methods will not wait

更新:添加了一个更简单的演示 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 调用
  • searchconsole.log('filtering data')并将搜索条件应用于 javascript object
  • render会将结果放在页面上。

重要的是,直到getData中的 ajax 调用完成后, search才会触发。

所以,现在我有了这个 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()

然后getData可以调用

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

jQuery 调用将正确地给我然后processing然后filtering data

这个 function 给了我filtering data ,一个错误,然后processing ,因为我似乎无法让链( .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')
    })
  }())

而且无论我做什么,链条都会在等待结束之前继续。


我得到了 XMLHttpRequest 的代码来工作。 方法链( .getData().search(...).render() )适用于此,因为这不允许 ajax function 在请求完成和执行回调之前返回。 **我仍然更愿意让.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)

这对我来说很难理解,所以如果其他人有同样的问题,我想分享一下。

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

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

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

这是相同代码的工作版本,使用then及其工作方式/原因,以及稍短的版本,因为显然可以使用 fetch 指定 await 。

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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM