回調地獄概念圖: javascript
回調地獄:在咱們須要對一個異步操做進行頻繁的調用的時候,且要保證一步操做的順序,可能會出現.java
var fs=require('fs');
/* 注意這裏的fs.readFile是一個異步任務,因此這裏他們輸出的順序並非 按照代碼的書寫順序,他們讀取文件的輸出順序,跟文件資源的大小還有其餘的元素有很大的關係 */
fs.readFile('./data/a.txt','utf8',function(err,data){
if(err){
/* 拋出異常 1.阻止程序的執行 2.把錯誤的消息打印到控制檯 */
throw err
}
console.log(data);
});
fs.readFile('./data/b.txt','utf8',function(err,data){
if(err){
/* 拋出異常 1.阻止程序的執行 2.把錯誤的消息打印到控制檯 */
throw err
}
console.log(data);
});
fs.readFile('./data/c.txt','utf8',function(err,data){
if(err){
/* 拋出異常 1.阻止程序的執行 2.把錯誤的消息打印到控制檯 */
throw err
}
console.log(data);
});
複製代碼
經過回調嵌套的方式保證順序:ajax
var fs = require('fs');
/* 注意這裏的fs.readFile是一個異步任務,因此這裏他們輸出的順序並非 按照代碼的書寫順序,他們讀取文件的輸出順序,跟文件資源的大小還有其餘的元素有很大的關係 */
fs.readFile('./data/a.txt', 'utf8', function (err, data) {
if (err) {
/* 拋出異常 1.阻止程序的執行 2.把錯誤的消息打印到控制檯 */
throw err
}
console.log(data);
fs.readFile('./data/b.txt', 'utf8', function (err, data) {
if (err) {
/* 拋出異常 1.阻止程序的執行 2.把錯誤的消息打印到控制檯 */
throw err
}
console.log(data);
fs.readFile('./data/c.txt', 'utf8', function (err, data) {
if (err) {
/* 拋出異常 1.阻止程序的執行 2.把錯誤的消息打印到控制檯 */
throw err
}
console.log(data);
});
});
});
複製代碼
回調地獄的缺點: 1)代碼的可維護性很是差,不利於代碼的閱讀 2)層層嵌套,代碼複雜.promise
爲了解決以上編碼方式帶來的問題(回調地獄嵌套),因此在ECMAScript 6 中新增了一個API:promise Promise的英文就是承諾 保證的意思bash
概念 : Promise是ES6中的新語法,Promise是一個構造函數,每一個new 出來的Promis實例對象都表明一個異步操做.異步
注意:使用promise並不會減小代碼量
複製代碼
簡單建立:async
// Promise是構造函數
// Promise.prototype上有.then() .catch .finally(),由於他綁定到了原型上,因此根據原型鏈的查找規則,他的實例對象也可使用這個方法
// Promise表示異步操做
// 下面的這個代碼表示建立了一個形式上的異步操做
// 經過new Promise()的時候,提供了一個function函數,在function函數中,能夠執行具體的異步操做
const p=new Promise(function(){
// 在這個function中能夠執行具體的異步操做
// 好比讀文件,或發送ajax
// fs.readFile()
})
複製代碼
.then方法的使用:函數
const fs=require('fs');
// 在ES6中新增了一個API Promise
// Promise 是一個構造函數
// 建立promise容器
// 1.給別人一個承諾
// Promise容器一旦建立,就開始執行裏面的代碼,
// 承諾自己不是異步的,可是內部每每都是封裝一個異步任務
var p1=new Promise(function(resolve,rejected){
fs.readFile('./data/a.txt','utf8',function(err,data){
if(err){
// 失敗了,承諾容器中的任務失敗了
// console.log(err);
// 把容器的Pending狀態改變爲Rejected
//調用reject就至關於調用了then方法的第二個參數
rejected(err);
}else{
// 承諾容器中的任務成功了
// console.log(data);
// 把容器的Pending狀態改成Resolved
// 也就是說這裏調用的resolve方法實際上就是then方法傳遞的function
resolve(data);
}
});
});
// p1就是那個承諾
// 當p1成功了而後(then)作指定的操做
// then方法接收的function就是容器中的resolve
p1.then(function(data){
console.log(data);
},function(err){
console.log('讀取文件失敗了...',err);
})
複製代碼
const fs = require('fs');
function pReadFile(filePath){
return new Promise(function (resolve, rejected) {
fs.readFile('./data/a.txt', 'utf8', function (err, data) {
if (err) {
rejected(err);
} else {
resolve(data);
}
});
});
}
pReadFile('./data/a.txt')
.then(function(data){
console.log(data);
return pReadFile('./data/b.txt');
})
.then(function(data){
console.log(data);
return pReadFile('./data/c.txt');
})
.then(function(data){
console.log(data);
})
//能夠經過.catch方法,捕獲前面全部的.then方法發生的錯誤,幾種處理
.catch(function(err){
console.log(err.message)
})
複製代碼
Promise代碼圖示:優化
形式上,Generator 函數是一個普通函數,可是有兩個特徵。一是,function關鍵字與函數名之間有一個星號;二是,函數體內部使用yield表達式,定義不一樣的內部狀態(yield在英語裏的意思就是「產出」) ES7 中的 async 和 await 能夠簡化 Promise 調用,提升 Promise 代碼的 閱讀性 和 理解性;ui
// 若是某個方法內部用到了await關鍵字,那麼這個方法必須被修飾爲異步async方法
// await只能用在被async修飾的方法中
async function test(){
// 若是某個方法的返回值是Promise的實例對象,就能夠用await修飾Promise實例
// await只能用在被async修飾的方法中
const data=await getContentPath('./files/1.txt').catch(err=>err);
if(data instanceof Error){
console.log('文件讀取失敗')
}else{
console.log(data);
}
console.log(data);
const data2=await getContentPath('./files/2.txt');
console.log(data2);
const data3=await getContentPath('./files/3.txt');
console.log(data3);
}
// 這是異步方法,可是並非純粹的異步方法
// 在異步方法中,遇到第一個await以前,全部的代碼都是同步執行的
test();
複製代碼
注意:async和await通常是同步使用的,二者缺一不可,
複製代碼