半理解系列--Promise的進化史

半理解系列--Promise的進化史

學過js的都知道,程序有同步編程和異步編程之分,同步就比如流水線,一步一個腳印,作完上個任務才作下一個,異步編程比如客服,客服接了一個電話,收到了一個任務,而後把任務交給另外的人來處理,同時,繼續接聽下一個電話,等到另外的人處理完任務了,再通知客服,客服再反饋給第一個打電話的人。異步編程通常用來調取接口拉數據。html

經過我描述的篇幅,就知道異步編程比同步編程麻煩許多。遠古時期,異步編程是經過回調函數來解決的。可是回調函數會有回調地獄的問題,回調的多了,維護人員看起來頭都大了,比如:taskC須要等待taskB作完(taskC才執行),taskB又須要等待taskA作完(taskB才執行)es6

function taskA (cb) {
  //..do task A
  cb()
}
function taskB(cb){
  //..do task B
  cb()
}
function taskC(cb){
  //..do task C
  cb()
}
taskA(function(){
  taskB(function(){
    taskC()
  })
})
...以此類推,不斷循環嵌套,最終陷入地獄

而Promise就把這一系列的回調,經過鏈式調用的方式鏈接起來,看起來清爽多了。一樣是上面的代碼,Promise能夠這麼寫(僞代碼)編程

new Promise().then(taskA).then(taskB).then(taskC)

promise的使用

const promise = new Promise((resolve,reject)=>{
  if (/*do something done*/){
    resolve() // 可在此傳參數
  } else {
    // do something fail
    reject() // 可在此傳參數
  }
})
promise.then(()=>{
  //do something
}).catch(e => { throw e})

上面的resolve,能夠看成task函數的cb回調函數,當resolve()執行的時候,then方法中的回調會被執行,若是是reject執行,錯誤會被catch捕捉。promise

Promise的靜態方法異步

上面說的thencatch都是Promise的原型方法,即Promise.prototype.then/catch
Promise自己有兩個靜態方法,其做用相似 new Promise()

Promise.resolve()async

const promise1 = Promise.resolve()

等價於

const promise2 = new Promise((reslove)=>{
  reslove()
})

使用該方法調用then方法
Promise.reject()異步編程

const promise1 = Promise.reject()

等價於

const promise2 = new Promise((resolve,reject)=>{
  reject()
})

使用該方法會被catch捕捉函數

Promise的鏈式調用this

Promise的實例對象的then方法是能夠重複調用的,then方法返回的是一個promise實例對象,因此能夠重複調用then方法,而且(敲黑板),上一個then方法的返回值,會做爲下一個then方法的參數傳遞google

舉個栗子:

const promise = Promise.resolve('start')

promise.then((params)=>{
  console.log(params) // start
  return 'aa'
}).then((params) => {
  console.log(params) // aa
  return 'bb'
}).then((params)=>{
  console.log(params) // bb
  return 'cc'
})

// 最後會返回一個狀態是resolve(cc)的promise對象:Promise {<resolved>: "cc"}

深刻一下(又不會懷孕)

function badAsyncCall() {
  var promise = Promise.resolve();
  promise.then(function() {
      // 任意處理
      return 'newVar';
  });
  return promise;
}
// 修改一下
function goodAsyncCall() {
  var promise = Promise.resolve();
  return promise.then(function() {
      // 任意處理
      return 'newWar';
  });
}

以上兩個寫法是否是很類似,惟一不一樣的就是return的處理。但調用,badAsynccall會出錯,而anAsyncCall能正確執行,好比:

badAsyncCall().then(params => { console.log('bad--',params)}) // bad-- undefined
goodAsyncCall().then(params => { console.log('good--',params)}) // good-- newWar

分析:第一種,錯誤寫法,首先在 promise.then 中產生的異常不會被外部捕獲,此外,也不能獲得 then 的返回值,即便其有返回值。
緣由:因爲每次 promise.then 調用都會返回一個新建立的promise對象,第一種返回的promise,至關於沒有調用過函數內部的then方法,是一個全新的promise實例對象
結論: 統一使用promise鏈式調用,如:promise.then(taskA).then(taskB)

### async&await和promise的前世緣緣

promise說白了仍是用回調的方式來解決異步問題,跟真正同步仍是有差距的。
異步編程的最高境界,就是根本不用關心它是否是異步!(來之ruanyifeng老師的話)

因此,async&await方案出現了

用法:

function readFile(fileName) {
  return new Promise((resolve,reject)=>{
    fs.readFile(fileName, function(error, data) {
      if (error) return reject(error);
      resolve(data); // 向thenc傳送異步讀取文件的數據
    });
  })
}

// 調用

readFile(fileName).then(function(data){
  console.log('prmoise read files data --', data)
})


// 等價於

async function asyncFn(fileName){
  const data = await readFile(fileName)
  console.log('await data --', data)
  return data
}

asyncFn(fileName)

寫法是否是簡潔了許多!
其實async就是一個Promise的語法糖,它的返回值是一個promise對象,所以能夠用then方法作鏈式調用(但參數就是async函數中的返回值,如上文的data!!)

async函數中還能夠不使用promise,好比:

async function asyncFn(){
  const data = await setTimeout(function(){
    console.log('setTimeout') 
    return 'data'
  },1000)
  console.log('data',data) // Timeout {} 對象
}
console.log('async',asyncFn()) // Promise { <pending> }

但這二者,其實常常混用,常見的就是readFile函數的作法啦

看懂以上的,才你們出一道題看看能不能懂;

async function asynFn(){ 
  await Promise.resolve('aaa')
  const data = {
    b:'bb',
    c:function(){ return this.b }
  }
  return data  //return 做爲參數傳遞給then then的chain鏈也是經過return參數來不斷傳遞給後面的then
}
var cball = asynFn()
cball.then(function(data){
  console.log('data:',data)
})

還有一種異步編程的語法糖: * & yield
跟async基本同樣,不在本文討論的重點。有興趣自行google啦

參考資料:
async 函數的含義和用法
Promise 對象

相關文章
相關標籤/搜索