點我看看~前端
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跟着變化