建立一個 readFile.js,讀取三個文件abc的內容並輸出到控制檯ajax
var fs = require('fs') fs.readFile('./a.txt','utf-8',function (err,data) { if(err) throw err //js語法,拋出異常,阻止程序執行,把錯誤打印到控制檯
console.log(data) //將a.txt內容輸出到控制檯
}) fs.readFile('./b.txt','utf-8',function (err,data) { if(err) throw err console.log(data) }) fs.readFile('./c.txt','utf-8',function (err,data) { if(err) throw err console.log(data) })
通常來講文件內容少的會先輸出,但不必定,因爲讀取文件是異步操做,因此沒法保證abc的輸出順序編程
若想abc按順序輸出,就須要將代碼嵌套promise
var fs = require('fs') fs.readFile('./a.txt','utf-8',function (err,data) { if(err) throw err console.log(data) //打印a.txt後再去讀取b.txt
fs.readFile('./b.txt','utf-8',function (err,data) { if(err) throw err console.log(data) //打印b.txt後再去讀取c.txt
fs.readFile('./c.txt','utf-8',function (err,data) { if(err) throw err console.log(data) }) }) })
像這樣,在異步編程中,造成了回調函數嵌套,嵌套過多時被稱爲回調地獄(callback hell),此方式雖然能讓異步操做按順序執行,但十分不利於閱讀和維護瀏覽器
爲了解決回調地獄嵌套編碼方式帶來的問題,ES6 新增了一個 API 叫 Promise,是一個構造函數異步
Promise上有兩個函數叫resolve(成功後的回調函數)和reject(失敗後的回調函數)mongoose
Promise原型裏有一個then方法,因此只要是Promise建立的實例均可訪問then方法異步編程
Promise 容器存在一個異步任務,任務只有三種狀態:Pending 爲正在執行,Resolve 爲已解決,Reiected 爲失敗函數
示例ui
var fs = require('fs') //Promise容器一旦建立就開始執行裏面的代碼
var p = new Promise(function (resolve,reject) { fs.readFile('./a.txt','utf-8',function (err,data) { if(err){ reject(err) //把容器的Pending狀態變爲Rejected
}else{ resolve(data) //把容器的Pending狀態改成Resolve
} }) }) //如何獲取容器成功和失敗的數據,就要用到p實例對象的then方法
//then方法接收的第一個function就是容器中的resolved,第二個function是reject
//兩個function的參數就是上面容器resolve和reject傳來的data和err.若resolve(123),則這裏data就是123
p.then(function (data) { console.log(data)
},function (err) { consoel.log('文件讀取失敗',err) })
Promise容器的建立不是異步的,但內部每每傳入異步任務編碼
封裝 Promise 版本的 readFile.js
var fs = require('fs') function readFile(filePath) { //將Promise實例對象返回
return new Promise(function (resolve,reject) { fs.readFile(filePath,'utf-8',function (err,data) { if(err){ reject(err) }else{ resolve(data) } }) }) } //由於返回的都是Promise的實例對象,因此可鏈式調用then
readFile('./a.txt') .then(function (data) { console.log(data) //data就是上面resolve傳來的a文件的內容
//而後將新的數據(err和data)經過resolve和reject傳遞給下一個then的function
return readFile('./b.txt') }) .then(function (data) { console.log(data) return readFile('./c.txt') }) .then(function (data) { console.log(data) })
封裝 Promise 版本的 ajax 方法
function get(url) { return new Promise(function (resolve, reject) { var xhr = new XMLHttpRequest() xhr.open('get', url) xhr.send() xhr.onload = function () { resolve(xhr.responseText) //成功則把獲取的數據放resolve這個回調函數中
} xhr.onerror = function (err) { reject(err) //失敗則把失敗信息放reject裏
} }) } get('./a.txt') .then(function (data) { console.log(data) return get('./b.txt') }) .then(function (data) { console.log(data) return get('./c.txt') }) .then(function (data) { console.log(data) })
若是前面的promise執行失敗,不想讓後續的promise操做被終止,可爲每一個promise指定失敗的回調,而後在失敗回調裏return新的promise
若是後續的promise執行依賴於前面的,前面的失敗了,則後面的沒有繼續執行下去的意義時,可捕獲異常
get('./a.txt') .then(function (data) { console.log(data) return get('./b.txt') }) .then(function (data) { console.log(data) return get('./c.txt') }) .then(function (data) { console.log(data) }) .catch(function(err){ //上面不要寫失敗回調
console.log(err.message) })
若是前面有任何的promise執行失敗則會當即終止全部promise執行並馬上進入catch中
這就是捕獲異常的兩種方式,根據需求,可選擇失敗回調或catch
模擬 jQuery 的 get 方法
jq中的ajax()返回的是Promise實例,因此可直接點then()
既能使用 Promise 也可使用回調函數嵌套的方式,只需加多一個回調函數便可
function get(url,callback) { return new Promise(function (resolve, reject) { var xhr = new XMLHttpRequest() xhr.open('get', url) xhr.send() xhr.onload = function () { callback(xhr.responseText) resolve(xhr.responseText) //成功則把獲取的數據放resolve這個回調函數中
} xhr.onerror = function (err) { callback(err) reject(err) //失敗則把失敗信息放reject裏
} }) } /*get('./a.txt') .then(function (data) { console.log(data) return get('./b.txt') }) .then(function (data) { console.log(data) return get('./c.txt') }) .then(function (data) { console.log(data) })*/ get('./a.txt',function (data) { console.log(data) get('./b.txt',function (data) { console.log(data) get('./c.txt',function (data) { console.log(data) }) }) })
瀏覽器、Node、mongoose全部 API 都支持 Promise