先講一下什麼是回撥地獄->callback hell,給大家看一張圖片,回撥地獄就是非同步裡面套了非同步,下面的非同步請求依賴於上一個非同步請求的結果,必須巢狀進去,放在回撥裡面,就形成了回撥地獄。為了解決這一問題,es6出現了一個api,就是promise.
promise其實就是一個建構函式,我們typeof promise ,就能得出一個"function",也可以把它理解為一個容器,這個容器中存放了一個非同步任務。它有三種狀態,Pending(進行中),resolved(已成功),rejected(已失敗)。下面我們建立一個promise容器
Promise容器一旦建立,就開始執行裡面的程式碼
var fs = require('fs')
new Promise(function(){
fs.readFile('./a.txt','utf8,function(err,data)'{
if(err){
//失敗了,承諾容器中的任務失敗了
console.log(err)
}else{
//承認容器中的任務成功了
console.log(data)
}
})
})
複製程式碼
下面我們寫一個輸出,
console.log(1)
new Promise(function(){
console.log(2)
fs.readFile('./a.txt','utf8,function(err,data)'{
if(err){
//失敗了,承諾容器中的任務失敗了
console.log(err)
}else{
console.log(3)
//承認容器中的任務成功了
console.log(data)
}
})
})
console.log(4)
複製程式碼
輸出的結果是什麼? 1 2 4 3 為什麼會輸出這樣的呢?js執行順序不是從上到下麼?下面我們簡單分析下: 先輸出1,緊接著遇到了new promise,立即執行,輸出2,我們上邊提到了promise裡面的任務是一個非同步,所以執行輸出4,當非同步執行完後輸出3
在此強調下:promise本身不是非同步,但是內部往往都是封裝一個非同步任務。
下面我們繼續,上邊提到了promise有成功和失敗的狀態,所以我們把上邊new promise的程式碼改寫下。
var p1=new Promise(function(resolve,reject){
fs.readFile('./a.txt','utf8,function(err,data)'{
if(err){
//失敗了,承諾容器中的任務失敗了
//把容器的Pending狀態變為失敗Rejected
//呼叫reject就相當於呼叫了then方法的第二個引數函式
reject(err)
}else{
//承認容器中的任務成功了
//console.log(data)
//把容器的Pending狀態變為成功resoved
//也就是說這裡呼叫的resolve方法實際上就是then方法傳遞的那個function
resolve(data)
}
})
})
複製程式碼
上面的程式碼描述了我們圖中promise的概念,promise裡面有一個非同步任務,預設的狀態pending成功了變成resoved,失敗了變成rejected。 那麼我們如何使用它呢?
//p1就是promise
//當p1成功瞭然後(then)做指定的操作
//then方法接收的function就是容器中的resovle函式
p1
.then(function(data){
console.log(data)
},function(err){
console.log('讀取失敗了',err)
})
複製程式碼
以上是promise的基礎語法, 我用圖來表示下
下面我們來說下解決回撥地獄問題:p1
.then(function(data)){
//當p1讀取成功的時候,當前函式中return的結果就可以在後面的then中function接收到,
//當你return 123後面就接收到123
// return 'hello' 後面就接收到'hello'
//沒有return 後面收到的就是undefined
//其實我們真正用到的是可以return 一個promise物件
//當return 一個Promise物件的時候,後續的then中的方法的第一個引數會作為p2的resolve
// return 123
return p2
},function(err){
console.log(err)
})
.then(function(data){
console.log(data) //在此接收p2的返回值
})
var p2=new Promise(function()){
fs.readFile('./a.txt','utf8,function(err,data)'{
if(err){
reject(err)
}else{
resolve(data)
}
})
})
複製程式碼
我們也可以多新增幾個then去返回多個new Promise,這個就是then的鏈式呼叫 上圖來描述下,
下面我們封裝一個readFile,
var fs = require('fs')
function pReadFile(filePath) {
return new Promise(function (resolve,reject) {
fs.readFile(filePath,'utf8',function (err,data) {
if(err){
reject(err)
}else{
resolve(data)
}
}
}
}
pReadFile('./data/test.txt')
.then(function(data){
console.log(data)
return pReadFile('./data/a.txt')
})
.then(function(data){
console.log(data)
return pReadFile('./data/b.txt')
})
.then(function(data){
console.log(data
})
複製程式碼
以上是一個簡單完整的小例子。 現在我們封裝一個promise的ajax方法。
function pGet(url,callback){
return new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest()
xhr.onload = function(){
callback && callback(JSON.parse(pGet.responseText))
resolve(JSON.parse(pGet.responseText))
}
xhr.onerror = function(){
reject(err)
}
xhr.open("get",url,true)
xhr.send()
}
}
複製程式碼