1.為什么要使用promise
/*
需求:異步的按順序去讀取1.txt、2.txt、3.txt文件的內容
假設 1.txt內容為111 、2.txt內容為222、3.txt內容為333
*/
var fs = require('fs');、
fs.readFile('./files/1.txt','utf8',function(err,data){
if(err){
throw err;
}
console.log(data);
})
fs.readFile('./files/1.txt','utf8',function(err,data){
if(err){
throw err;
}
console.log(data);
})
fs.readFile('./files/1.txt','utf8',function(err,data){
if(err){
throw err;
}
console.log(data);
})
/*
結果:111 222 333。(好像滿足需求啊)
*/
/*
問題:上面的代碼雖然正常按照順序讀取了文件,因為文件的內容都非常少,io讀取耗時少,但當2.txt內容較多時候,2.txt的內容是最后輸出的。因為他們都是異步操作,不知道誰先讀取完。 這得取決於異步任務IO的耗時。
*/
/*
常規解決辦法:
在第一個異步任務讀取成功之后再讀取第二個異步任務,
第二成功后,在讀取第三個異步任務
*/
//讀取第一個異步任務
fs.readFile('./files/1.txt','utf8',function(err,data){
if(err){
throw err;
}
console.log(data);
//讀取第二個異步任務
fs.readFile('./files/2.txt','utf8',function(err,data){
console.log(data);
//讀取第三個異步任務
fs.readFile('./files/3.txt','utf8',function(err,data){
console.log(data);
})
})
})
/*
結果: 111 222 333 (這必須按照順序輸出的,結果杠桿的)
*/
/*
問題:
以上按照順序執行多個異步任務產生的問題:`回調地獄`問題(層層包裹進行回調,代碼也不夠優雅)
*/
/*
解決辦法:采用es6,提供的promise來解決上述產生的問題。
*/
2.Promise基本使用
promise介紹:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
//簡單說promise就是用來執行異步任務的,可以解決上面所說的回調地獄問題,
//語法:new Promise(function(callback){})
var fs = require('fs');
var promise = new Promise(function(resolve,reject){
//這里就是寫異步的代碼,只要new Promise操作,就會立刻執行這里的代碼
//兩個參數 resolve 異步執行成功的回調函數,reject異步執行失敗的回調函數
fs.readFile('./files/1.txt', 'utf8', function (err, data) {
if (err) {
throw err;
}
console.log(data);
})
});
//運行結果:111
3.promise參數resolve和reject
兩個參數
- resolve 異步執行成功的回調函數,
- reject異步執行失敗的回調函數
var fs = require('fs');
var promise = new Promise(function (resolve, reject) {
//兩個參數: resolve 成功的回調函數名 , reject失敗的回調函數名
fs.readFile('./files/111.txt', 'utf8', function (err, data) {
if (err) {
//說明失敗了,要執行失敗的回調函數
reject(err);
} else {
//成功的邏輯
resolve(data);
}
//等價於
// err ? reject(err) : resolve(data);
})
});
//new Promise返回的是一個promise對象,
//這個對象有一個方法叫做then,在其原型對象上
//通過這then方法可以指定成功和失敗的回調函數
//語法:promise.then(successCallback,errorCallback);
promise.then(function (data) {
//then第一個函數是成功的回調,參數是resolve(err)中的data
console.log('成功:' + data); // 若成功,運行結果:成功:111
}, function (err) {
//then第二個函數是失敗的回調函數,參數是reject(err)中的err錯誤對象
console.log('失敗:' + err);
});
4.解決回調地獄問題
//封裝一個異步讀取文件的內容的函數
//此函數返回對應異步任務的promise對象
function getFileByPath(path) {
return new Promise(function (resolve,reject) {
fs.readFile(path, 'utf8', function (err, data) {
if(err){
reject(err); //失敗
}else{
resolve(data); //成功
}
})
});
}
//由於then通過getFileByPath返回的是一個promise對象,所以可以繼續.then串聯調用(鏈式調用)
getFileByPath('./files/1.txt')
.then(function(data){
console.log("成功:"+data);
return getFileByPath('./files/2.txt');
},function(err){
console.log("失敗:"+err);
return getFileByPath('./files/2.txt');
})
.then(function(data){
console.log("成功:"+data);
return getFileByPath('./files/3.txt');
},function(err){
console.log("失敗:"+err);
return getFileByPath('./files/3.txt');
})
.then(function(data){
console.log("成功:"+data);
},function(err){
console.log("失敗:"+err);
});
注意:上面一定會保證按照.then的順序去執行異步的代碼,
如果某個異步任務有錯誤則會觸發對應then的第二個錯誤的回調函數。即每個promise對象都有對應的錯誤回調,對其他的promise不影響。畢竟promise的英文翻譯就是保證。
5.catch的使用
catch也是在Promise.prototype原型上定義的。
image.png
需求:如果多個promise任務,其中有一個失敗了,則終止后面的所有的promise執行
getFileByPath('./files/1.txt')
.then(function(data){
console.log("成功:"+data);
return getFileByPath('./files/2.txt');
}) //上面的then通過getFileByPath返回的是一個promise對象,所以可以繼續.then串聯調用(鏈式調用)
.then(function(data){
console.log("成功:"+data);
return getFileByPath('./files/3.txt');
})
.then(function(data){
console.log("成功:"+data);
})
.catch(function(err){
// catch作用: 上面所有的promise如果其中一個有錯誤,
//則終止下面所有的promise執行,且直接進入到catch中獲取對應promise的異常錯誤信息
console.log('catch:'+err);
})
注意:如果在then中定義了錯誤回調則不會進入到上面的catch中,這是因為promise對象指定了對應的錯誤處理回調。
6 then-catch-finally
var fs = require('fs');
var promise = new Promise(function (resolve, reject) {
//兩個參數: resolve 成功的回調函數名 , reject失敗的回調函數名
fs.readFile('./files/11.txt', 'utf8', function (err, data) {
if (err) {
//說明失敗了,要執行失敗的回調函數
reject(err);
} else {
//成功的邏輯
resolve(data);
}
})
});
//new Promise返回的是一個promise對象,
//這個對象有一個方法叫做then,在其原型對象上
//通過這then方法可以指定成功和失敗的回調函數
//語法:promise.then(successCallback,errorCallback);
promise.then(function (data) {
//then第一個函數是成功的回調,參數是resolve(err)中的data
console.log('成功:' + data); // 若成功,運行結果:成功:111
}).catch(function(err){
//then第二參數錯誤回調換成這里catch也行,兩者選其一
console.log('err');
}).finally(function(){
//無論失敗成功都會執行
console.log('完成');
})
7. Promise.all靜態方法的使用
參照:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise#%E6%96%B9%E6%B3%95
需求:把所有的promise執行成功的結果保存在一個數組中。。
var promise1 = getFileByPath('./files/1.txt');
var promise2 = getFileByPath('./files/2.txt');
var promise3 = getFileByPath('./files/3.txt');
//執行多個異步任務,
Promise.all([promise3,promise1,promise2]).then(function(data){
console.log(data);
},function(err){
console.log('錯誤了:'+err);
})
//Promise.all尤其是在一個頁面上發起多個ajax請求的時候,如果要同時保證他們成功,則使用Promise.all是最合適不過的了。其中一個失敗則也可以在then的第二個回調做失敗的邏輯。
若都成功,運行結果:['333','111','22']
注意:Promise.all的成功結果是返回一個數組,且數組中數據的結果順序與Promise.all數組的傳參順序是一樣的。