什么是promise?我們用Promise來解決什么問題?


一、什么是Promise?我們用Promise來解決什么問題?

Promise 是異步編程的一種解決方案: 從語法上講,promise是一個對象,從它可以獲取異步操作的消息;從本意上講,它是承諾,承諾它過一段時間會給你一個結果。 promise有三種狀態: pending(等待態),fulfiled(成功態),rejected(失敗
態);狀態一旦改變,就不會再變。創造promise實例后,它會立即執行
 

為什么有Promises這個東西


  • 同步的方式寫異步的代碼,用來解決回調地獄問題。
  • 此外,promise對象提供統一的接口,使得控制異步操作更加容易。

什么是Promise?


  • Promise,簡單說就是一個容器,里面保存着某個未來才會結束的事件(通常是一個異步操作)的結果。
  • 從語法上說,promise 是一個對象,從它可以獲取異步操作的的最終狀態(成功或失敗)。
  • Promise是一個構造函數,對外提供統一的 API,自己身上有all、reject、resolve等方法,原型上有then、catch等方法。

Promise的兩個特點


  1. Promise對象的狀態不受外界影響

    1)pending 初始狀態

    2)fulfilled 成功狀態

    3)rejected 失敗狀態

    Promise 有以上三種狀態,只有異步操作的結果可以決定當前是哪一種狀態,其他任何操作都無法改變這個狀態

  2. Promise的狀態一旦改變,就不會再變,任何時候都可以得到這個結果,狀態不可以逆,只能由 pending變成fulfilled或者由pending變成rejected

使用 new 來創建一個promise對象。


Promise接受一個「函數」作為參數,該函數的兩個參數分別是resolvereject。這兩個函數就是就是「回調函數」

resolve函數的作用:在異步操作成功時調用,並將異步操作的結果,作為參數傳遞出去;

reject函數的作用:在異步操作失敗時調用,並將異步操作報出的錯誤,作為參數傳遞出去。

const promise = new Promise((resolve, reject) => { // do something here ... if (success) { resolve(value); // fulfilled } else { reject(error); // rejected } }); 

 

Promise的API


then()方法

then 方法就是把原來的回調寫法分離出來,在異步操作執行完后,用鏈式調用的方式執行回調函數。

而 Promise 的優勢就在於這個鏈式調用。我們可以在 then 方法中繼續寫 Promise 對象並返回,然后繼續調用 then 來進行回調操作。

可有兩個參數,第一個是成功 resolve 調用的方法,第二個是失敗 reject 調用的方法
下面做一個買筆寫作業上交的演示,它們是層層依賴的關系,下一步的的操作需要使用上一部操作的結果。(這里使用 setTimeout 模擬異步操作),正式開發可以用 ajax 異步

        //買筆 function buy(){ console.log("開始買筆"); var p = new Promise(function(resolve,reject){ setTimeout(function(){ console.log("買了筆芯"); resolve("數學作業"); },1000); }); return p; } //寫作業 function work(data){ console.log("開始寫作業:"+data); var p = new Promise(function(resolve,reject){ setTimeout(function(){ console.log("寫完作業"); resolve("作業本"); },1000); }); return p; } function out(data){ console.log("開始上交:"+data); var p = new Promise(function(resolve,reject){ setTimeout(function(){ console.log("上交完畢"); resolve("得分:A"); },1000); }); return p; } /* 不建議使用這種方式 buy().then(function(data){ return work(data); }).then(function(data){ return out(data); }).then(function(data){ console.log(data); });*/ //推薦這種簡化的寫法 buy().then(work).then(out).then(function(data){ console.log(data); }); 

 

在這里插入圖片描述

正式開發用ajax異步:

var promise = new Promise(function(resolve,reject){ $.ajax({ url:'/api/poisearch.json', method:'get', datatype:'json', success:(res) =>{ resolve(res) }, error:(err)=>{ reject(err) } }); }); promise.then(function(res){ return res.data }).then(function(data){ return data.result; }).then(function(result){ console.log(result) }); //推薦使用箭頭函數簡寫成,極大提升了代碼的簡潔性和可讀性 promise.then(res => res.data).then(data => data.result).then(result => console.log(result)); 

 

Promise構造函數的超能力

Promises寫法的本質就是把異步寫法寫成同步寫法。傳入Promise構造函數的函數參數會第一優先執行,無論這個函數多么的繁復,有多少層回調,有多少秒的計數器,統統都會最優先執行。
也就是說,我們只要new了一個Promise(),那么Promise構造函數的函數參數其實是同步代碼,但是.then比較特殊,.then會等到promise對象實例有了結果(resolved或者rejected),.then()里面代碼才會執行。鏈條上的每一個.then都會等前面的promise有了結果才會執行,Promise構造函數的這個超能力是Promises系統的威力之源。

reject()方法:

上面樣例我們通過 resolve 方法把 Promise 的狀態置為完成態(Resolved),這時 then 方法就能捕捉到變化,並執行“成功”情況的回調。
而 reject 方法就是把 Promise 的狀態置為已失敗(Rejected),這時 then 方法執行“失敗”情況的回調(then 方法的第二參數)

function rebuy(){ console.log("開始買筆"); var p = new Promise(function(resolve,reject){ setTimeout(function(){ console.log("買筆失敗"); reject("沒帶夠錢"); },1000); }); return p; } function rework(data){ console.log("開始寫作業:"+data); var p = new Promise(function(resolve,reject){ setTimeout(function(){ console.log("寫完作業"); resolve("作業本"); },1000); }); return p; } rebuy().then(rework,function(data){ console.log(data); }); 

 

在這里插入圖片描述

catch()方法:

  1. 它可以和 then 的第二個參數一樣,用來指定 reject 的回調
function rebuy(){ console.log("開始買筆"); var p = new Promise(function(resolve,reject){ setTimeout(function(){ console.log("買筆失敗"); reject("沒帶夠錢"); },1000); }); return p; } function rework(data){ console.log("開始寫作業:"+data); var p = new Promise(function(resolve,reject){ setTimeout(function(){ console.log("寫完作業"); resolve("作業本"); },1000); }); return p; } rebuy().then(rework).catch(function(data){ console.log(data); }); 

 

在這里插入圖片描述

  1. 它的另一個作用是,當執行 resolve 的回調(也就是上面 then 中的第一個參數)時,如果拋出異常了(代碼出錯了),那么也不會報錯卡死 js,而是會進到這個 catch 方法中。
function buy(){ console.log("開始買筆"); var p = new Promise(function(resolve,reject){ setTimeout(function(){ console.log("買了筆芯"); resolve("數學作業"); },1000); }); return p; } function work(data){ console.log("開始寫作業:"+data); var p = new Promise(function(resolve,reject){ setTimeout(function(){ console.log("寫完作業"); resolve("作業本"); },1000); }); return p; } buy().then(function(data){ throw new Error("買了壞的筆芯"); work(data); }).catch(function(data){ console.log(data); }); 

 

在這里插入圖片描述

all()方法:

Promise 的 all 方法提供了並行執行異步操作的能力,並且在所有異步操作執行完后才執行回調。

比如下面代碼,兩個個異步操作是並行執行的,等到它們都執行完后才會進到 then 里面。同時 all 會把所有異步操作的結果放進一個數組中傳給 then。

        //買作業本 function cutUp(){ console.log('挑作業本'); var p = new Promise(function(resolve, reject){ //做一些異步操作 setTimeout(function(){ console.log('挑好購買作業本'); resolve('新的作業本'); }, 1000); }); return p; } //買筆 function boil(){ console.log('挑筆芯'); var p = new Promise(function(resolve, reject){ //做一些異步操作 setTimeout(function(){ console.log('挑好購買筆芯'); resolve('新的筆芯'); }, 1000); }); return p; } Promise.all([cutUp(),boil()]).then(function(results){ console.log("寫作業的工具都買好了"); console.log(results); }); 

 

 

在這里插入圖片描述

race()方法:
race 按字面解釋,就是賽跑的意思。race 的用法與 all 一樣,只不過 all 是等所有異步操作都執行完畢后才執行 then 回調。而 race 的話只要有一個異步操作執行完畢,就立刻執行 then 回調。

注意:其它沒有執行完畢的異步操作仍然會繼續執行,而不是停止。

這里我們將上面樣例的 all 改成 race

 Promise.race([cutUp(), boil()]).then(function(results){ console.log("哈哈,我先買好啦"); console.log(results); }); 

 

在這里插入圖片描述

race 使用場景很多。比如我們可以用 race 給某個異步請求設置超時時間,並且在超時后執行相應的操作。

請求某個圖片資源

function requestImg(){ var p = new Promise(function(resolve, reject){ var img = new Image(); img.onload = function(){ resolve(img); } img.src = 'xxxxxx'; }); return p; } //延時函數,用於給請求計時 function timeout(){ var p = new Promise(function(resolve, reject){ setTimeout(function(){ reject('圖片請求超時'); }, 5000); }); return p; } Promise.race([requestImg(), timeout()]).then(function(results){ console.log(results); }).catch(function(reason){ console.log(reason); }); //上面代碼 requestImg 函數異步請求一張圖片,timeout 函數是一個延時 5 秒的異步操作。我們將它們一起放在 race 中賽跑。 //如果 5 秒內圖片請求成功那么便進入 then 方法,執行正常的流程。 //如果 5 秒鍾圖片還未成功返回,那么則進入 catch,報“圖片請求超時”的信息。 

 

在這里插入圖片描述

在工作中的應用

  1. 傳統回調模式
/*** 第一步:找到北京的id 第二步:根據北京的id -> 找到北京公司的id 第三步:根據北京公司的id -> 找到北京公司的詳情 目的:模擬鏈式調用、回調地獄 ***/ // 回調地獄 // 請求第一個API: 地址在北京的公司的id $.ajax({ url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/city', success (resCity) { let findCityId = resCity.filter(item => { if (item.id == 'c1') { return item } })[0].id $.ajax({ // 請求第二個API: 根據上一個返回的在北京公司的id “findCityId”,找到北京公司的第一家公司的id url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/position-list', success (resPosition) { let findPostionId = resPosition.filter(item => { if(item.cityId == findCityId) { return item } })[0].id // 請求第三個API: 根據上一個API的id(findPostionId)找到具體公司,然后返回公司詳情 $.ajax({ url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/company', success (resCom) { let comInfo = resCom.filter(item => { if (findPostionId == item.id) { return item } })[0] console.log(comInfo) } }) } }) } }) 

 

promise模式


// Promise 寫法 // 第一步:獲取城市列表 const cityList = new Promise((resolve, reject) => { $.ajax({ url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/city', success (res) { resolve(res) } }) }) // 第二步:找到城市是北京的id cityList.then(res => { let findCityId = res.filter(item => { if (item.id == 'c1') { return item } })[0].id findCompanyId().then(res => { // 第三步(2):根據北京的id -> 找到北京公司的id let findPostionId = res.filter(item => { if(item.cityId == findCityId) { return item } })[0].id // 第四步(2):傳入公司的id companyInfo(findPostionId) }) }) // 第三步(1):根據北京的id -> 找到北京公司的id function findCompanyId () { let aaa = new Promise((resolve, reject) => { $.ajax({ url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/position-list', success (res) { resolve(res) } }) }) return aaa } // 第四步:根據上一個API的id(findPostionId)找到具體公司,然后返回公司詳情 function companyInfo (id) { let companyList = new Promise((resolve, reject) => { $.ajax({ url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/company', success (res) { let comInfo = res.filter(item => { if (id == item.id) { return item } })[0] console.log(comInfo) } }) }) }
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM