學過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)
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的靜態方法異步
上面說的then
和catch
都是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啦