Promise

1楚昭、準備

1.1栖袋、區(qū)別實例對象與函數(shù)對象

1.實例對象: new函數(shù)產(chǎn)生的對象,稱為實例對象抚太,簡稱為對象
2.函數(shù)對象:將函數(shù)作為對象使用時塘幅,簡稱為函數(shù)對象

    function Fn(){//Fn函數(shù)

    };
    let fn = new Fn();//Fn是構(gòu)造函數(shù)昔案、fn是實例對象(簡稱為對象)
    console.log(Fn.prototype);//Fn是函數(shù)對象
    Fn.bind({});//Fn是函數(shù)對象
    $("#test");//jQuery 函數(shù)
    $.get("/test");//jQuery函數(shù)對象

1.2、兩種類型的回調(diào)函數(shù)

1.2.1晌块、同步回調(diào)

1爱沟、理解: 立即執(zhí)行,完全執(zhí)行完了才結(jié)束,不會放入回調(diào)隊列中
2匆背、例子: 數(shù)組遍歷相關(guān)的回調(diào)函數(shù)呼伸、Promise的 excutor函數(shù)


例一、數(shù)組遍歷相關(guān)的回調(diào)函數(shù)
let arr = ["a","b","c"];
arr.forEach(r => { //遍歷回調(diào)函數(shù)
    console.log("11111");
});
console.log("222222222");

例二钝尸、Promise的 excutor函數(shù)
new Promise(resolve => {
    console.log("11111");
})
console.log("222222");

1.2.2括享、異步回調(diào)

1、理解: 不會立即執(zhí)行珍促,會放入回調(diào)隊列中將來執(zhí)行
2铃辖、例子: 定時器回調(diào) / ajax回調(diào) / Promise的成功|失敗的回調(diào)

例一、定時器回調(diào)
setTimeout(() =>{
    console.log("11111");
}, 0);
console.log("222222222");

例二猪叙、Promise的成功|失敗的回調(diào)
new Promise(resolve => {
    resolve("")
}).then(res => {
    console.log("1111");
})
console.log("222222");

1.3娇斩、JS 的 error 處理

1.3.1、錯誤的類型

1穴翩、Error: 所有錯誤的父類型
2犬第、ReferenceError: 引用的變量不存在
3、TypeError: 數(shù)據(jù)類型不正確的錯誤
4芒帕、RangeError: 數(shù)據(jù)值不在其所允許的范圍內(nèi)
5.歉嗓、SyntaxError: 語法錯誤

// 1、Error: 所有錯誤的父類型
// 2背蟆、ReferenceError: 引用的變量不存在
    console.log(a);//ReferenceError: a is not defined
    console.log("----------");//沒有捕獲 error,下面的代碼不會被執(zhí)行(這行代碼沒有被執(zhí)行)

// 3带膀、TypeError: 數(shù)據(jù)類型不正確的錯誤
    let b;
    // console.log(b.xxx);//TypeError: Cannot read property 'xxx' of null
    b = {};
    b.xxx();//TypeError: b.xxx is not a function

// 4志珍、RangeError: 數(shù)據(jù)值不在其所允許的范圍內(nèi)
    function fn(){
        fn();
    };
    fn();//RangeError: Maximum call stack size exceeded

// 5.、SyntaxError: 語法錯誤
    const c = """";//SyntaxError: Unexpected string

1.3.2本砰、錯誤處理

1休弃、捕獲錯誤:try ... catch
2霹琼、拋出錯誤:throw error

//1、捕獲錯誤:try ... catch
try{
    let b;
    console.log(b.xxx );
} catch (error){
    console.log(error.message);
    console.log(error.stack);
}
console.log("出錯之后洁灵,通過捕獲錯誤莺琳,程序能繼續(xù)處理");

//2还棱、拋出錯誤:throw error
function something(){
    if(Date.now()%2 == 1){
        console.log("當前時間為奇數(shù),可以執(zhí)行任務");
        something()
    }else{//如果時間是偶數(shù)拋出異常,由調(diào)用來處理
        throw new Error("當前時間為偶數(shù),無法執(zhí)行任務");
    }
}
try{
    something()
} catch(error){
    alert(error.message)
}

2惭等、Promise的理解與使用

2.1珍手、Promise是什么?

2.1.1、理解

抽象表達

1琳要、promise 是ES6中提供的一個異步編程的新的解決方案(舊的是誰?)

具體表達

1寡具、從語法上來說: Promise是一個構(gòu)造函數(shù)
2、從功能上來說: promise對象用來封裝一個異步操作稚补,并可以獲取其結(jié)果


2.1.2童叠、Promise 狀態(tài)與狀態(tài)的改變

Promise對象有三種狀態(tài):

  • 待定(pending): 初始狀態(tài),既沒有被兌現(xiàn)课幕,也沒有被拒絕厦坛。
  • 已兌現(xiàn)(fulfilled): 意味著操作成功完成。
  • 已拒絕(rejected): 意味著操作失敗乍惊。

這三種狀態(tài)的變化途徑只有2種:
1杜秸、pending 變?yōu)?resolved
2、pending 變?yōu)?rejected
?說明:只有這2種润绎,且一個promise對象只能改變一次
???無論變?yōu)槌晒€是失敗撬碟,都會有一個結(jié)果數(shù)據(jù)
???成功的結(jié)果數(shù)據(jù)一般稱為value,失敗的結(jié)果數(shù)據(jù)一般稱為reason


2.1.3莉撇、Promise 的基本流程

MDN Promise基本流程圖

注意: 如果一個 promise 已經(jīng)被兌現(xiàn)(fulfilled)或被拒絕(rejected)呢蛤,那么我們也可以說它處于已敲定(settled)狀態(tài)。您還會聽到一個經(jīng)常跟 promise 一起使用的術(shù)語:已決議(resolved)稼钩,它表示 promise 已經(jīng)處于已敲定(settled)狀態(tài)顾稀,或者為了匹配另一個 promise 的狀態(tài)被"鎖定"了。Domenic Denicola 的 States and fates 中有更多關(guān)于 promise 術(shù)語的細節(jié)可以供您參考坝撑。

簡化的 Promise基本流程圖


2.1.4静秆、Promise 的基本使用

   // 1、創(chuàng)建一個新的 Promise 對象
    const ret = new Promise((resolve,reject) => {//執(zhí)行器函數(shù)
        // 2巡李、執(zhí)行異步操作任務
        setTimeout(() => {
            const time = Date.now();//如果當前時間是偶數(shù)就代表成功,否則代表失敗
            // 3.1抚笔、如果成功了,調(diào)用resolve(value)
            if(time % 2 == 0){
                resolve("成功的數(shù)據(jù)侨拦,time=" + time);
            }else{
                // 3.2殊橙、如果失敗了,調(diào)用reject(reason)
                reject("失敗的數(shù)據(jù)狱从,time=" + time);
            }
        })
    })

    // ret.then(
    //     value => {//接收得到成功的 value 數(shù)據(jù)
    //         console.log("成功的回調(diào)-----------", value);
    //     },
    //     reason => {//接收得到失敗的 reason 數(shù)據(jù)
    //         console.log("失敗的回調(diào)-----------", reason);
    //     }
    // )

    // promise簡化了對error的處理膨蛮,上面的代碼我們也可以這樣寫:
    ret.then(//接收得到成功的 value 數(shù)據(jù)
        value => {
            console.log("成功的回調(diào)-----------", value);
        }
    ).catch(//接收得到失敗的 reason 數(shù)據(jù)
        reason => {
            console.log("失敗的回調(diào)-----------", reason);
        }
    )

2.2、為什么要使用 Promise季研?

1. 指定異步回調(diào)函數(shù)的方式更加靈活:
舊的: 必須在啟動異步任務前指定
promise: 啟動異步任務 => 返回promie對象 => 給promise對象綁定回調(diào)函數(shù)(甚至可以在異步任務結(jié)束后指定)

// 舊的指定回調(diào)函數(shù)
function successBcak(){
    console.log("成功的回調(diào)");
}
function failBcak(){
    console.log("失敗的回調(diào)");
}
function person(age, successBcak, failBcak){
    setTimeout(() => {
        if(age > 0 && age < 100){
            successBcak();
        }else{
            failBcak();
        }
    },1000)
}
console.log("aaaaaa");
person(18, successBcak, failBcak);//在調(diào)用前必須指定回調(diào)函數(shù)敞葛,而 Promise不需要
console.log("bbbbbbb");

// Promise
const p = new Promise((resolve, reject) => {
    console.log('執(zhí)行 executor同步函數(shù)')
    let time = Date.now();
    setTimeout(() => {//Promise可以先執(zhí)行異步任務,再指定回調(diào)函數(shù)
        if (time % 2 === 0) {
            console.log("resolve")
            resolve(time)
        } else {
            console.log("reject")
            reject(time)
        }
    }, 2000)
})

setTimeout(() => {
    p.then(value => {
        console.log('value', value)
    }, reason => {
        console.log('reason', reason)
    })
}, 3000)
console.log("aaaaaaaa");

2与涡、 支持鏈式調(diào)用, 可以解決回調(diào)地獄問題
什么是回調(diào)地獄? 回調(diào)函數(shù)嵌套調(diào)用, 外部回調(diào)函數(shù)異步執(zhí)行的結(jié)果是嵌套的回調(diào)函數(shù)執(zhí)行的條件
回調(diào)地獄的缺點? 不便于閱讀 / 不便于異常處理
解決方案? promise鏈式調(diào)用
終極解決方案? async/await

回調(diào)地獄
$.ajax({
    url:"查詢用戶",
    success:function(res){
        console.log(res)
        // 將數(shù)據(jù)渲染到頁面上
        $.ajax({
            url: "查詢到課程",
            success:function(res){
                // 獲取到了課程內(nèi)容的數(shù)據(jù),渲染到頁面上
                $.ajax({})
            }
        })
    }
})

promise 處理回調(diào)地獄
new Promise(function(resolve, reject){
    $.ajax({
        url:"查詢用戶",
        success:function(res){
                resolve(res)
        },
        error:function(err){
                reject(err)
        }   
    })
}).then(res=>{
    console.log(res)
        return new Promise(function(resolve,rejeect){
            $.ajax({
                    url: "查詢到課程",
                    success:function(res1){
                            // 獲取到了文章詳細內(nèi)容的數(shù)據(jù),渲染到頁面上
                            resolve(res1)   
                    },
                    error:function(err){
                        reject(err)
                    }   
            })
        })
}).then(res=>{
    console.log(res)
    return new Promise(function(resolve,rejeect){
            $.ajax({
                    url: "查詢到分數(shù)",
                    success:function(res1){
                            // 獲取到了文章詳細內(nèi)容的數(shù)據(jù),渲染到頁面上
                            resolve(res1)   
                    },
                    error:function(err){
                        reject(err)
                    }   
            })
        })
}).then(res=>{
    console.log(res)
})

封裝一下 Promise
function get(url) {
    return new Promise((resolve, reject) => {
        $.ajax({
            url: url,
            success: function (data) {
                resolve(data);
            },
            error: function (err) {
                reject(err)
            }
        })
    });
}

//調(diào)用封裝后的方法 
get("查詢用戶")
    .then((data) => {
        console.log("用戶查詢成功~~~:", data)
        return get("查詢到課程);
    })
    .then((data) => {
        console.log("課程查詢成功~~~:", data)
        return get("查詢到分數(shù));
    })
    .then((data) => {
        console.log("課程成績查詢成功~~~:", data)
    }).catch((err) => {
        console.log("出現(xiàn)異常", err)
    });


async function foo() {
  try {
    const result = await doSomething();
    const newResult = await doSomethingElse(result);
    const finalResult = await doThirdThing(newResult);
    console.log(`Got the final result: ${finalResult}`);
  } catch(error) {
    failureCallback(error);
  }
}

2.3惹谐、如何使用 Promise持偏?(常用的 Promise API)

Promise.all:(promises) =>{}
promises:包含n個promise的數(shù)組
重點:返回一個新的promise,只有所有的promise都成功才成功氨肌,只要有一個失敗了就直接失敗

Promise.all([p1,p2,......]).then(values => {
    console.log(values);
}).catch(error => {
    consloe.log(error);
})

開發(fā)中常用的Promise.all場景鸿秆,獲取兩個接口成功的返回值后,再執(zhí)行下面的操作

function p1(){
    return new Promise((resolve) => {
        setTimeout(() => {//接口一
            resolve("p1")
        },2000)
    })
}
function p2(){
    return new Promise((resolve,reject) => {
        setTimeout(() => {//接口二
            resolve("p2")
        },1000)
    })
}
Promise.all([p1(),p2()]).then(values =>{
    console.log(values)
}).catch(error =>{
    console.log("err",error);
})

Promise.race: (promises) =>{}
promises:包含n個promise的數(shù)組
重點:返回一個新的promise怎囚,說明:返回一個新的promise卿叽,第一個完成的promise的結(jié)果狀態(tài)就是最終的結(jié)果狀態(tài)

Promise.race([p1,p2,......]).then(values => {
    console.log(values);
}).catch(error => {
    consloe.log(error);
})
function p1(){
        return new Promise((resolve) => {
            setTimeout(() => {
                resolve("p1")
            },1000)
        })
    }
    function p2(){
        return new Promise((resolve,reject) => {
            setTimeout(() => {
                reject("p2")
            },2000)
        })
    }
    Promise.race([p1(),p2()]).then(values =>{
        console.log(values)
    }).catch(error =>{
        console.log("err",error);
    })

3、async 與 await

MDN文檔:
async 函數(shù):https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function
await:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/await

async await 的作用
簡化promise對象的使用:不用再使用then()來指定成功/失敗的回調(diào)函數(shù)以同步編碼(沿有回凋函數(shù)了)方式實現(xiàn)異步流程

3.1桩了、async 函數(shù)

1附帽、函數(shù)的返回值為promise對象
2.、promise對象的結(jié)果由async函數(shù)執(zhí)行的返回值決定

// async 函數(shù)的返回值是一個promise對象
async function fn1(){
    // 同步
    // return 1;
    // throw 2;
    // return Promise.resolve(3);
    // return Promise.reject(4);

    // 異步
    return new Promise((resolve,reject) =>{
        setTimeout(()=> {
            resolve(5)
        },2000)
    })
}
let ret1 = fn1();
console.log(ret1);
// async 函數(shù)的返回值是一個promise對象井誉,所以需要通過.then(()=>{})獲取函數(shù)的返回值
ret1.then(res => {
    console.log("then", res)
}).catch(err =>{
    console.log("catch", err)
})

3.2蕉扮、 await表達式

await 操作符用于等待一個 Promise 對象。它只能在異步函數(shù) Promise 中使用
1颗圣、await右側(cè)的表達式一般為 promise 對象喳钟,但也可以是其它的值
2、如果表達式是promise對象, await 返回的是 promise 成功的值(所以獲取 promise 失敗的返回值在岂,會使用try{} catch (error) {})

function fn2(){
    return new Promise((resolve,reject) => {
        setTimeout(()=> {
            // resolve(11111)
            reject(22222)
        },2000)
    })
}
function fn3(){
    return 444444;
}
async function getResult(){
    try {
        let val = await fn2();//await右側(cè)表達為promise對象奔则,得到的結(jié)果就是promise對象成功的value
        console.log("getResult",val);
    } catch (error) {
        console.log("失敗的結(jié)果",error);
    }

    // let val2 = await fn3();///await右側(cè)表達不是promise對象,得到的結(jié)果就是它本身
    // console.log("val2",val2)
}
getResult();
async與await配合使用
function fn2(){
    return new Promise((resolve,reject) => {
        setTimeout(()=> {
            resolve(11111)
            // reject(22222)
        },2000)
    })
}
async function fn3(){//async 函數(shù)的返回值是一個promise對象
    return 444444;
}
async function getResult(){
    try {
        let val = await fn2();//await右側(cè)表達為promise對象,得到的結(jié)果就是promise對象成功的value
        let val2 = await fn3();
        console.log("getResult",val);
        console.log("getResult",val2);
    } catch (error) {
        console.log("失敗的結(jié)果",error);
    }
}
getResult();
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蔽午,一起剝皮案震驚了整個濱河市易茬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌及老,老刑警劉巖抽莱,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異骄恶,居然都是意外死亡食铐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門僧鲁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來虐呻,“玉大人,你說我怎么就攤上這事寞秃≌宓穑” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵春寿,是天一觀的道長犁柜。 經(jīng)常有香客問我,道長堂淡,這世上最難降的妖魔是什么馋缅? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮绢淀,結(jié)果婚禮上萤悴,老公的妹妹穿的比我還像新娘。我一直安慰自己皆的,他們只是感情好覆履,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著费薄,像睡著了一般硝全。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上楞抡,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天伟众,我揣著相機與錄音,去河邊找鬼召廷。 笑死凳厢,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的竞慢。 我是一名探鬼主播先紫,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼筹煮!你這毒婦竟也來了遮精?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤败潦,失蹤者是張志新(化名)和其女友劉穎本冲,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體变屁,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡眼俊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了粟关。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疮胖。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖闷板,靈堂內(nèi)的尸體忽然破棺而出澎灸,到底是詐尸還是另有隱情,我是刑警寧澤遮晚,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布性昭,位于F島的核電站,受9級特大地震影響县遣,放射性物質(zhì)發(fā)生泄漏糜颠。R本人自食惡果不足惜汹族,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望其兴。 院中可真熱鬧顶瞒,春花似錦、人聲如沸元旬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽匀归。三九已至坑资,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間穆端,已是汗流浹背袱贮。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留徙赢,地道東北人字柠。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像狡赐,于是被迫代替她去往敵國和親窑业。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容