使用thunk有条件地执行异步不会执行两次

时间:2017-08-10 01:46:56

标签: javascript callback closures thunk

我跟随Kyle Simpson Rethinking Asynchronous JavaScript视频课程,并对他的thunk模式如何使用闭包感到困惑。代码是这样的:

function ajax(url, cb) {
  $.ajax({ url: `text/${url}`, type: 'GET', dataType: 'json' })
  .done(data => {
    cb(data)
  })
}

function getFile(file) {
  var text, fn;
  ajax(file,function(response){
    if (fn) {
      fn(response)
    } else {
      text = response
    }
  })
  return function th(cb) {
    if (text) {
      cb(text)
    } else {
      fn = cb
    }
  }
}

var th1 = getFile('1')
var th2 = getFile('2')
var th3 = getFile('3')

th1(function ready(text){
  console.log(text)
  th2(function ready(text){
    console.log(text)
    th3(function ready(text){
      console.log(text)
      th2(function (text) {
        console.log(text)
      })
    })
  })
})

我在最后的嵌套部分添加了对th2的额外调用。我期望使用闭包来返回最初从th2打印的值,存储在text函数中的闭包变量getFile中,即不进行另一次网络调用。虽然事实并非如此:在t3回调中打印文本后执行停止。

为什么这个闭包没有返回已经检索到的值?

1 个答案:

答案 0 :(得分:0)

  

我期望使用闭包来返回最初从th2打印的值,该值存储在text函数中的闭包变量getFile中。虽然事实并非如此:在t3回调中打印文本后执行停止。

这些thunk的问题是你不能使用它们两次(至少,当第一次使用是异步时)。该值永远不会存储在闭包变量text中。为什么?因为th2是在ajax调用成功之前第一次运行的,它运行了

if (text) // still empty
  …
else // nope, nothing yet, store for later
 fn = cb

然后,当ajax调用回调时,它只会运行

if (fn) // we got something to call
  fn(response)
…

而不是text = response。因此,当第二次调用th2时(或者更糟糕的是,立即从回调中调用),它将再次尝试将cb存储在fn变量中,但什么都不会叫那个。

可能的解决方法是

… // in the thunk closure
ajax(file, function(response) {
  text = response;
  if (fn) {
    fn(response)
  }
})

相反会使你的代码工作,但仍然会被破坏:如果在异步th2回调之前多次调用ajax会怎么样?然后cb覆盖先前的fn,因此最终我们需要维护一系列应该调用的回调。好吧,这样做并为回调添加可链接性,并且you've got the most basic Promise implementation已经。