Rumah  >  Artikel  >  hujung hadapan web  >  Mari kita bincangkan tentang kemahiran pengaturcaraan tak segerak JavaScript again_javascript

Mari kita bincangkan tentang kemahiran pengaturcaraan tak segerak JavaScript again_javascript

WBOY
WBOYasal
2016-05-16 15:17:351151semak imbas

Dengan pembangunan bahagian hadapan, perkataan tak segerak menjadi semakin biasa. Katakan kita kini mempunyai tugas tak segerak:

Mulakan beberapa permintaan ke pelayan, dan keputusan setiap permintaan digunakan sebagai parameter untuk permintaan seterusnya.
Mari lihat apa yang perlu kita lakukan:

Panggil balik

Perkara pertama yang terlintas di fikiran dan yang paling biasa digunakan ialah fungsi panggil balik. Mari kita buat enkapsulasi ringkas:

let makeAjaxCall = (url, cb) => {
  // do some ajax
  // callback with result
}

makeAjaxCall('http://url1', (result) => {
  result = JSON.parse(result)
})

Hmm, nampak cantik! Tetapi apabila kami cuba menyusun berbilang tugas, kodnya kelihatan seperti ini:

makeAjaxCall('http://url1', (result) => {
  result = JSON.parse(result)

  makeAjaxCall(`http://url2?q=${result.query}`, (result) => {
    result = JSON.parse(result)

    makeAjaxCall(`http://url3?q=${result.query}`, (result) => {
      // ...
    })
  })
})

Ya tuhanku! Biarkan longgokan itu }) pergi ke neraka!

Jadi, kami ingin mencuba menggunakan model acara JavaScript:

1. Pub/Sub

Dalam pemprosesan acara DOM, Pub/Sub ialah mekanisme yang sangat biasa Contohnya, kita perlu menambahkan pemantauan acara pada elemen:

elem.addEventListener(type, (evt) => {
  // handler
})

Jadi bolehkah kita membina model yang serupa untuk mengendalikan tugas tak segerak?

Perkara pertama ialah membina pusat pengedaran dan menambah kaedah on / emit:

let PubSub = {
  events: {},
  on(type, handler) {
    let events = this.events
    events[type] = events[type] || []
    events[type].push(handler)
  },
  emit(type, ...datas) {
    let events = this.events

    if (!events[type]) {
      return
    }

    events[type].forEach((handler) => handler(...datas))
  }
}

Kemudian kita boleh menggunakannya seperti ini:

const urls = [
  'http://url1',
  'http://url2',
  'http://url3'
]

let makeAjaxCall = (url) => {
  // do some ajax
  PubSub.emit('ajaxEnd', result)
}

let subscribe = (urls) => {
  let index = 0

  PubSub.on('ajaxEnd', (result) => {
    result = JSON.parse(result)

    if (urls[++index]) {
      makeAjaxCall(`${urls[index]}?q=${result.query}`)
    }
  })

  makeAjaxCall(urls[0])
}

Nampaknya tiada perubahan revolusioner berbanding dengan fungsi panggil balik, tetapi kelebihannya ialah kita boleh meletakkan fungsi permintaan dan pemprosesan dalam modul yang berbeza untuk mengurangkan gandingan.

2. Janji

Perubahan revolusioner sebenar ialah spesifikasi Promise. Dengan Promise, kami boleh menyelesaikan tugas tak segerak seperti ini:

let makeAjaxCall = (url) => {
  return new Promise((resolve, reject) => {
    // do some ajax
    resolve(result)
  })
}

makeAjaxCall('http://url1')
  .then(JSON.parse)
  .then((result) => makeAjaxCall(`http://url2?q=${result.query}`))
  .then(JSON.parse)
  .then((result) => makeAjaxCall(`http://url3?q=${result.query}`))

Hebat! Ia ditulis seperti fungsi segerak!

Jangan risau, anak muda. Kami mempunyai lebih baik lagi:

3. Penjana

Satu lagi pembunuh besar ES6 ialah Penjana[2]. Dalam fungsi penjana, kita boleh mengganggu pelaksanaan fungsi melalui pernyataan hasil, dan mengulangi penyataan melalui kaedah seterusnya di luar fungsi Lebih penting lagi, kita boleh menyuntik data ke dalam fungsi melalui kaedah seterusnya untuk mengubah tingkah laku secara dinamik fungsi. Contohnya:

function* gen() {
  let a = yield 1
  let b = yield a * 2
  return b
}

let it = gen()

it.next() // output: {value: 1, done: false}
it.next(10) // a = 10, output: {value: 20, done: false}
it.next(100) // b = 100, output: {value: 100, done: true}

Merangkum fungsi makeAjaxCall kami sebelum ini melalui penjana:

let makeAjaxCall = (url) => {
  // do some ajax
  iterator.next(result)
}

function* requests() {
  let result = yield makeAjaxCall('http://url1')
  result = JSON.parse(result)
  result = yield makeAjaxCall(`http://url2?q=${result.query}`)
  result = JSON.parse(result)
  result = yield makeAjaxCall(`http://url3?q=${result.query}`)
}

let iterator = requests()
iterator.next() // get everything start

Oh! Logiknya nampak sangat jelas, tetapi rasanya sangat tidak selesa untuk perlu menyuntik iterator dari luar setiap kali...

Jangan risau, mari kita campurkan Promise dan Generator dan lihat apakah ilmu hitam yang akan dihasilkan:

let makeAjaxCall = (url) => {
  return new Promise((resolve, reject) => {
    // do some ajax
    resolve(result)
  })
}

let runGen = (gen) => { 
  let it = gen()

  let continuer = (value, err) => {
    let ret

    try {
      ret = err ? it.throw(err) : it.next(value)
    } catch (e) {
      return Promise.reject(e)
    }

    if (ret.done) {
      return ret.value
    }

    return Promise
      .resolve(ret.value)
      .then(continuer)
      .catch((e) => continuer(null, e))
  }

  return continuer()
}

function* requests() {
  let result = yield makeAjaxCall('http://url1')
  result = JSON.parse(result)
  result = yield makeAjaxCall(`http://url2?q=${result.query}`)
  result = JSON.parse(result)
  result = yield makeAjaxCall(`http://url3?q=${result.query}`)
}

runGen(requests)

Fungsi runGen kelihatan seperti automaton, sangat hebat!

Sebenarnya, kaedah runGen ini adalah pelaksanaan fungsi async ECMAScript 7:

4. fungsi tak segerak

Dalam ES7, fungsi async ciri yang lebih semula jadi[3] diperkenalkan. Menggunakan fungsi async kita boleh menyelesaikan tugasan seperti ini:

let makeAjaxCall = (url) => {
  return new Promise((resolve, reject) => {
    // do some ajax
    resolve(result)
  })
}

;(async () => {
  let result = await makeAjaxCall('http://url1')
  result = JSON.parse(result)
  result = await makeAjaxCall(`http://url2?q=${result.query}`)
  result = JSON.parse(result)
  result = await makeAjaxCall(`http://url3?q=${result.query}`)
})()

Sama seperti apabila kami menggabungkan Janji dan Penjana di atas, kata kunci tunggu juga menerima Janji. Dalam fungsi async, penyataan yang selebihnya akan dilaksanakan hanya selepas penyata menunggu selesai Keseluruhan proses adalah sama seperti kita menggunakan fungsi runGen untuk merangkumkan Penjana.

Di atas ialah beberapa mod pengaturcaraan tak segerak JavaScript yang diringkaskan dalam artikel ini, saya harap ia akan membantu pembelajaran semua orang.

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn