Mari kita bincangkan tentang kemahiran pengaturcaraan tak segerak JavaScript again_javascript

WBOY
Lepaskan: 2016-05-16 15:17:35
asal
1164 orang telah melayarinya

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)
})

Salin selepas log masuk

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) => {
      // ...
    })
  })
})

Salin selepas log masuk

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
})
Salin selepas log masuk

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))
  }
}

Salin selepas log masuk

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])
}

Salin selepas log masuk

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}`))

Salin selepas log masuk

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}

Salin selepas log masuk

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

Salin selepas log masuk

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)

Salin selepas log masuk

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}`)
})()

Salin selepas log masuk

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.

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
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
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan