你與弄懂promise之間可能只差這篇文章(一)

點我看看~前端

promise誕生以前:vue

由於JS引擎在執行js代碼時只分配了一個線程去執行,因此Javascript是單線程的。因爲有這個前置設定,前端er在書寫代碼時繞不開的一件事是就是----如何處理異步,即處理「如今和稍後」關係的問題,事實上咱們每一天都在與異步邏輯打交道。node

在promise出現以前,前端er基本上都是經過callback的方式來解決「稍後」的問題,例若有經典的「發佈-訂閱」模式,觀察者模式,他們都運用了傳入回調函數的高階函數。vue2.x源碼在實現數據雙向綁定時就是運用的發佈-訂閱模式。promise

咱們先來看看三個例子。(例子均在node環境中運行, 其中name.txt中的內容是"kk", age.txt中的內容是10。)異步

1 . 回調函數(callback)。fs讀取文件的前後順序是不固定的,咱們沒法判斷哪一個文件先讀取完成。此例實現的是,在徹底讀取兩個文件的內容以後進行某個操做(例如console個啥的)。函數

let fs = require('fs');

let arr = [];
let after = (times, cb) => {
 return (data) => {
 arr.push(data);
 if (--times === 0) {
 cb(arr)
   }
 }
}

let on = after(2, (arr) => {
 console.log('我是在所有讀取了2個文件內容以後打印出來的, ', arr)
})
fs.readFile('name.txt', 'utf8', (err, data) => {
 on(data)
})

fs.readFile('age.txt', 'utf8', (err, data) => {
 on(data)
})

結果:
  我是在所有讀取了2個文件內容以後打印出來的, [ 'kk', '10' ]。

說明:
  這種寫法的問題在於,須要依靠計數來執行回調函數裏面的內容。咱們先得這計算出有幾個異步操做,而後統計出來在所有的異步操做完成後再執行回調。

2 .發佈-訂閱模式。訂閱的時候添加訂閱者,發佈的時候執行相應的訂閱函數。此例實現的是,在特定的時候emit了某事件,訂閱了該事件的回調函數繼而執行。ui

class EventEmitter {
  constructor () {
    this.subs = {}
  }
  on (eventName, cb) {
    if (!this.subs[eventName]) {
      this.subs[eventName] = []
    }
    this.subs[eventName].push((...args) => cb(...args))
  }
  emit (eventName, ...args) {
    if (this.subs[eventName]) {
       this.subs[eventName].forEach(cb => cb(...args))
    } else {
        throw Error(`沒有訂閱${eventName}這個事件`)
    }
   }
}

const event = new EventEmitter();
let fs = require('fs');
event.on('kk-event', (...args) => {
  fs.readFile('name.txt', 'utf8', (err, data) => {
    console.log('data1', data, ...args)
  })
})
event.on('kk-event', (...args) => {
  fs.readFile('age.txt', 'utf8', (err, data) => {
    console.log('data2', data, ...args)
  })
})
event.emit('kk-event', 123, 456)

結果:

data1 kk 123 456
data2 10 123 456

3 . 觀察者模式。它與發佈-訂閱二者本質是同樣的,只不過觀察者模式在寫法上強調觀察者和被觀察者之間的關係,而發佈-訂閱模式則沒有這樣的關係。此例實現的是,在被觀察者的狀態發生變化後,觀察者執行本身的update方法進行更新。this

class Subject {
  constructor() {
    this.observers = [];
    this.state = ''; // 假設觀察者觀察的是被觀察者的state
  }
  setState (status) { // 當state變化時出發觀察者的update方法
    this.state = status;
    this.notify();
  }
  attach (observer) {
    this.observers.push(observer)  // 與發佈-訂閱不一樣的是,這裏添加的是一個個觀察者實例,這就將被觀察者和觀察者之間關聯了起來
  }
  notify () {
    this.observers.forEach(observe => observe.update()) // 在被觀察者狀態變化時,調用更新的是觀察者的update方法
  }
}

class Observer {
  constructor (name, target) {
    this.name = name;
    this.target = target;
  }
  update () {
    console.log(`通知${this.name},被觀察者狀態變化,因此觀察者${this.name}跟着變化`)
  }
}

let fs = require('fs');
let subject = new Subject();
let observer1 = new Observer('kk1', subject);
let observer2 = new Observer('kk2', subject);
subject.attach(observer1);
subject.attach(observer2);
subject.setState('B');

結果:

通知kk1,被觀察者狀態變化,因此觀察者kk1跟着變化
通知kk2,被觀察者狀態變化,因此觀察者kk2跟着變化
相關文章
相關標籤/搜索