創建一個 readFile.js,讀取三個文件abc的內容並輸出到控制台
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按順序輸出,就需要將代碼嵌套
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(失敗后的回調函數)
Promise原型里有一個then方法,所以只要是Promise創建的實例都可訪問then方法
Promise 容器存在一個異步任務,任務只有三種狀態:Pending 為正在執行,Resolve 為已解決,Reiected 為失敗
示例
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
