摘要:等待十秒后代碼才算執(zhí)行完畢常見(jiàn)用法將定時(shí)器和異步操作放在一起,如果定時(shí)器先觸發(fā),則認(rèn)為超時(shí),告知用戶(hù)。
Promise
介紹:
用于異步計(jì)算
將異步操作隊(duì)列化,按照期望的順序執(zhí)行,返回符合預(yù)期的結(jié)果
可以在對(duì)象之間傳遞和操作promise,幫助我們處理隊(duì)列
由于promise是控制異步操作的,所以先來(lái)介紹一下在promise之前異步操作的常見(jiàn)語(yǔ)法。
事件偵聽(tīng)與響應(yīng)
回調(diào)函數(shù)(例如ajax請(qǐng)求回調(diào))
異步回調(diào)的問(wèn)題:
回調(diào)地獄問(wèn)題(一個(gè)回調(diào)嵌入一個(gè)回調(diào),特別是一些數(shù)據(jù)庫(kù)操作和文件操作 , 難以維護(hù))
無(wú)法正常使用 return 和 throw
異步回調(diào)的回調(diào)函數(shù)都是在一個(gè)新棧中,所以在現(xiàn)在的棧無(wú)法獲取到先前棧的信息。之前的棧也捕獲不到當(dāng)前棧拋出的錯(cuò)誤,所以在異步回調(diào)中無(wú)法正常使用try catch正常處理錯(cuò)誤。
在異步回調(diào)中經(jīng)常需要在外層回調(diào)中去定義一些變量給內(nèi)層回調(diào)使用。
talk is cheap , show me the code
const path = require("path"); const fs = require("fs"); //尋找最大文件的函數(shù) function findLargest(dir,callback){ fs.readdir(dir,function(err,files){ if(err) return callback(err); //[錯(cuò)誤使用回調(diào)來(lái)處理] let count = files.length; //獲取文件長(zhǎng)度 let errored = false; //是否錯(cuò)誤 let stats = []; //遍歷文件夾下的所有文件 files.forEach(file => { fs.stat(path.join(dir,file),(err,stat) =>{ if(errored) return; if(err){ errored = true; return callback(err); } stats.push(stat); if(--count === 0){ let largest = stats .filter(function(stat){ console.log("-----"); console.log(stat.isFile()); return stat.isFile(); }) //先判斷是否是文件 .reduce(function(prev,next){ //判斷大小 if(prev.size > next.size) { return prev; } return next; }); callback(null,files[stats.indexOf(largest)]) } }) }) }) } findLargest("../blog/blogDemo/移動(dòng)端滾動(dòng)詳解demo",function(err,filename){ if(err) return console.error(err); console.log("largest file was:",filename); })
上面就是一個(gè)查找最大文件的例子,其中有許多回調(diào)帶來(lái)的問(wèn)題。接下來(lái)我們先回歸主題,學(xué)習(xí)一些promise的使用,然后使用promise來(lái)改寫(xiě)這個(gè)例子。
promise詳解
new Promise( /*實(shí)例化Promise時(shí)傳入一個(gè)執(zhí)行器,也就是一個(gè)函數(shù)*/ function(resolve,reject){ //異步操作放在這里 resolve(); //處理成功,修改實(shí)例化的promise對(duì)象的狀態(tài)為fulfilled reject(); //處理失敗,修改實(shí)例化的promise對(duì)象的狀態(tài)為rejected } ) .then(function A(){ //成功之后的處理,即調(diào)用resolve()就執(zhí)行A中的內(nèi)容 },function B(){ //失敗之后的處理,即調(diào)用reject()或者拋出了錯(cuò)誤,就執(zhí)行B中的內(nèi)容 })
promise有三個(gè)狀態(tài):
pending 【待定】初始狀態(tài)
fulfilled 【實(shí)現(xiàn)】操作成功
rejected 【否決】操作失敗
promise的狀態(tài)一發(fā)生改變,立馬調(diào)用.then()中的響應(yīng)函數(shù)處理后續(xù)步驟,如果then()中返回了一個(gè)新的promise實(shí)例,則繼續(xù)循環(huán)下去。
promise常用的場(chǎng)景:
console.log("start"); new Promise(function(resolve,reject){ setTimeout(function(){ //定時(shí)器模擬異步 resolve("hello"); //修改promise狀態(tài)調(diào)用then中的第一個(gè)函數(shù) },2000); }).then((value)=>{ console.log(value); //接收resolve傳來(lái)的值 return new Promise(function(resolve){ //該then()返回一個(gè)新的promise實(shí)例,后面可以繼續(xù)接then setTimeout(function(){ resolve("world"); //修改新promise的狀態(tài),去調(diào)用then },3000) }) }).then((value)=>{ console.log(value); }) //輸出結(jié)果: /* 立即輸出 start 兩秒輸出 hello 再三秒 world */
上面我們?cè)?then() 函數(shù)中返回的是一個(gè)新的promise,如果返回的不是一個(gè)新的promise會(huì)怎樣呢?依然是上面的代碼,稍作修改。
console.log("start"); new Promise(function(resolve,reject){ setTimeout(function(){ resolve("hello"); },2000); }).then((value)=>{ console.log(value); (function(){ return new Promise(function(resolve){ setTimeout(function(){ resolve("world"); },3000) }) })(); return false; }).then((value)=>{ console.log(value); }) /* 結(jié)果: 立即輸出 start 兩秒輸出 hello 三秒輸出 flase */
根據(jù)上面的運(yùn)行結(jié)果來(lái)看,如果在一個(gè)then()中沒(méi)有返回一個(gè)新的promise,則return 什么下一個(gè)then就接受什么,在上面的實(shí)例代碼中return的是false,下一個(gè)then中接受到的value就是false,如果then中沒(méi)有return,則默認(rèn)return的是undefined.
注意:then中return Promise必須是在then函數(shù)的作用域中return,不能在其他函數(shù)作用域中return,無(wú)效。上面的例子中return Promise就是在一個(gè)立即執(zhí)行函數(shù)中返回的,所以無(wú)效。
.then()中包含.then()的嵌套情況
then()的嵌套會(huì)先將內(nèi)部的then()執(zhí)行完畢再繼續(xù)執(zhí)行外部的then();在多個(gè)then嵌套時(shí)建議將其展開(kāi),將then()放在同一級(jí),這樣代碼更清晰。
console.log("start"); new Promise((resolve,reject)=>{ setTimeout(function(){ console.log("step"); resolve(110); },1000) }) .then((value)=>{ return new Promise((resolve,reject)=>{ setTimeout(function(){ console.log("step1"); resolve(value); },1000) }) .then((value)=>{ console.log("step 1-1"); return value; }) .then((value)=>{ console.log("step 1-2"); return value; }) }) .then((value)=>{ console.log(value); console.log("step 2"); }) /* start step step1 step 1-1 step 1-2 110 step 2 */ //展開(kāi)之后的代碼 console.log("start"); new Promise((resolve,reject)=>{ setTimeout(function(){ console.log("step"); resolve(110); },1000) }) .then((value)=>{ return new Promise((resolve,reject)=>{ setTimeout(function(){ console.log("step1"); resolve(value); },1000) }) }) .then((value)=>{ console.log("step 1-1"); return value; }) .then((value)=>{ console.log("step 1-2"); return value; }) .then((value)=>{ console.log(value); console.log("step 2"); })
錯(cuò)誤處理
promise處理錯(cuò)誤有兩種方式,一種是發(fā)現(xiàn)錯(cuò)誤執(zhí)行then中的第二個(gè)回調(diào)函數(shù)來(lái)處理錯(cuò)誤,一種是.catch()來(lái)處理錯(cuò)誤
注意:拋出ERROR時(shí),只有在執(zhí)行函數(shù)的頂層拋出后面的catch才會(huì)接受到。這里如果在setTimeout中拋出錯(cuò)誤,catch和then中的錯(cuò)誤處理函數(shù)是接受不到的
//1.根據(jù)錯(cuò)誤執(zhí)行then中第二個(gè)回調(diào)來(lái)處理錯(cuò)誤 new Promise((resolve,reject)=>{ setTimeout(function(){ //只要出現(xiàn)了錯(cuò)誤或者調(diào)用了reject,就可以在then的第二個(gè)函數(shù)中獲取到 reject("err"); },1000) }).then((value)=>{ console.log(value); }, //出錯(cuò)之后的執(zhí)行函數(shù) (err)=>{ console.log("出錯(cuò)了"); console.log(err); }) /* 出錯(cuò)了 err */ //2.根據(jù)catch來(lái)獲取錯(cuò)誤,拋出err或者執(zhí)行reject()都會(huì)在catch中獲取到錯(cuò)誤信息 new Promise((resolve,reject)=>{ //只要出現(xiàn)了錯(cuò)誤,就可以在then的第二個(gè)函數(shù)中獲取到 setTimeout(function(){ reject("一個(gè)錯(cuò)誤"); },1000) }).then((value)=>{ console.log(value); }).catch(err=>{ console.log("錯(cuò)誤信息:"+err); }) /* 錯(cuò)誤信息:一個(gè)錯(cuò)誤 */
更推薦使用catch的方式進(jìn)行處理錯(cuò)誤,因?yàn)閏atch能獲取到之前所有then中出現(xiàn)的錯(cuò)誤
catch和then的連用
如果每一步都有可能出現(xiàn)錯(cuò)誤,那么就可能出現(xiàn)catch后面接上then的情況。上代碼
new Promise((resolve,reject)=>{ resolve(); }) .then(value=>{ console.log("done 1"); throw new Error("done 1 error"); }) .catch(err=>{ console.log("錯(cuò)誤信息1:"+err); }) .then(value=>{ console.log("done 2"); }) .catch(err=>{ console.log("錯(cuò)誤信息2:"+err); }) /* done 1 錯(cuò)誤信息1:Error: done 1 error done 2 說(shuō)明catch后面會(huì)繼續(xù)執(zhí)行then,catch返回的也是一個(gè)promise實(shí)例 */ new Promise((resolve,reject)=>{ resolve(); }) .then(value=>{ console.log("done 1"); throw new Error("done 1 error"); }) .catch(err=>{ console.log("錯(cuò)誤信息1:"+err); throw new Error("catch error"); }) .then(value=>{ console.log("done 2"); }) .catch(err=>{ console.log("錯(cuò)誤信息2:"+err); }) /* done 1 錯(cuò)誤信息1:Error: done 1 error 錯(cuò)誤信息2:Error: catch error 如果在catch中也拋出了錯(cuò)誤,則后面的then的第一個(gè)函數(shù)不會(huì)執(zhí)行,因?yàn)榉祷氐膒romise狀態(tài)已經(jīng)為rejected了 */
總的來(lái)說(shuō),catch之后可以接then,catch也是返回的一個(gè)promise對(duì)象。如果catch中出現(xiàn)錯(cuò)誤,則promise狀態(tài)修改成reject,否則為fullfilled狀態(tài)
Promise.all()
將多個(gè)Promise批量執(zhí)行,所有的Promise都完畢之后返回一個(gè)新的Promise。
接收一個(gè)數(shù)組作為參數(shù)
數(shù)組中可以是Promise實(shí)例,也可以是別的值,只有Promise會(huì)等待狀態(tài)的改變
所有子Promise完成,則該P(yáng)romise完成,并且返回值是參數(shù)數(shù)組中所有Promise實(shí)例的結(jié)果組成的數(shù)組
有任何一個(gè)Promise失敗,則該P(yáng)romise失敗,返回值是第一個(gè)失敗的Promise的結(jié)果
console.log("here we go"); Promise.all([1,2,3]) .then(all=>{ console.log("1: " + all); return Promise.all([function(){ console.log("ooxx"); },"xxoo",false]) }) .then(all=>{ console.log("2: " + all); let p1 = new Promise(resolve=>{ setTimeout(function(){ resolve("I"m p1"); },1500) }); let p2 = new Promise(resolve=>{ setTimeout(function(){ resolve("I"m p2"); },2000) }); return Promise.all([p1,p2]); }) .then(all=>{ console.log("3: "+all); let p1 = new Promise((resolve,reject)=>{ setTimeout(function(){ resolve("P1"); },1000) }) let p2 = new Promise((resolve,reject)=>{ setTimeout(function(){ reject("P2"); },3000) }) let p3 = new Promise((resolve,reject)=>{ setTimeout(function(){ reject("P3"); },2000) }) return Promise.all([p1,p2,p3]); }) .then(all=>{ console.log("all: " + all); }) .catch(err=>{ console.log("Catch:" + err); }) /* here we go 1: 1,2,3 2: function(){ console.log("ooxx"); },xxoo,false 3: I"m p1,I"m p2 Catch:P3 證明了上面的四點(diǎn)。 */
Promise.race()
和Promise.all()差不多,區(qū)別就是傳入的數(shù)組中有一個(gè)Promise完成了則整個(gè)Promise完成了。
let p1 = new Promise(resolve=>{ setTimeout(function(){ resolve("p1"); },10000); }) let p2 = new Promise(resolve=>{ setTimeout(function(){ resolve("p2"); },1000); }) Promise.race([p1,p2]) .then((value)=>{ console.log(value); }) /* p1 1s之后輸出 。。 等待十秒后代碼才算執(zhí)行完畢 */
常見(jiàn)用法:
將定時(shí)器和異步操作放在一起,如果定時(shí)器先觸發(fā),則認(rèn)為超時(shí),告知用戶(hù)。
let p1 = new Promise(resolve=>{ $.ajax({ success:function(result){ resolve(result); } }); //異步操作 }) let p2 = new Promise(resolve=>{ setTimeout(function(){ resolve("timeout"); },10000); }) Promise.race([p1,p2]) .then(value=>{ if(value === "timeout"){ alert("請(qǐng)求超時(shí)"); } })
將回調(diào)包裝為Promise
好處:1.可讀性好 2. 返回的結(jié)果可以放在任意Promise隊(duì)列
// 將nodejs中fs模塊的readDir和readFile方法包裝為Promise //FileSystem.js const fs = require("fs"); module.exports = { readDir: function(path,options){ return new Promise(resolve=>{ fs.readdir(path,options,(err,files)=>{ if(err){ throw err; } resolve(files); }) }) }, readFile: function(path,options){ return new Promise(resolve=>{ fs.readFile(path,options,(err,content)=>{ if(err) throw err; resolve(content); }) }) } } //test.js const fs = require("./FileSystem"); fs.readFile("./test.txt","utf-8") .then(content=>{ console.log(content); })
尋找最大文件Promise改版
const fs = require("fs"); const path = require("path"); const FileSystem = require("./FileSystem"); //用上面封裝的FileSystem function findLargest(dir) { return FileSystem .readDir(dir, "utf-8") .then(files => { return Promise.all(files.map(file=>{ return new Promise(resolve =>{ fs.stat(path.join(dir,file),(err,stat)=>{ if err throw err; if(stat.isDirectory()){ return resolve({ size: 0 }); } stat.file = file; resolve(stat); }); }); })); }) .then( stats =>{ let biggest = stats.reduce((memo,stat)=>{ if(memo.size < stat.size){ return stat; } return memo; }); return biggest.file; }) }
我的文章都可以在我的gitbook上找到。歡迎star哈哈!gitbook地址
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/95765.html
摘要:本身就是的語(yǔ)法糖。類(lèi)似于后面代碼會(huì)等內(nèi)部代碼全部完成后再執(zhí)行打印結(jié)果操作符用于等待一個(gè)對(duì)象。它只能在異步函數(shù)中使用。參考附在版本位中是可以直接使用的。持續(xù)更新中來(lái)點(diǎn)顆吧 async await本身就是promise + generator的語(yǔ)法糖。 本文主要講述以下內(nèi)容 async awiat 實(shí)質(zhì) async await 主要特性 async await 實(shí)質(zhì) 下面使用 pro...
摘要:本身就是的語(yǔ)法糖。類(lèi)似于后面代碼會(huì)等內(nèi)部代碼全部完成后再執(zhí)行打印結(jié)果操作符用于等待一個(gè)對(duì)象。它只能在異步函數(shù)中使用。參考附在版本位中是可以直接使用的。持續(xù)更新中來(lái)點(diǎn)顆吧 async await本身就是promise + generator的語(yǔ)法糖。 本文主要講述以下內(nèi)容 async awiat 實(shí)質(zhì) async await 主要特性 async await 實(shí)質(zhì) 下面使用 pro...
摘要:本身就是的語(yǔ)法糖。類(lèi)似于后面代碼會(huì)等內(nèi)部代碼全部完成后再執(zhí)行打印結(jié)果操作符用于等待一個(gè)對(duì)象。它只能在異步函數(shù)中使用。參考附在版本位中是可以直接使用的。持續(xù)更新中來(lái)點(diǎn)顆吧 async await本身就是promise + generator的語(yǔ)法糖。 本文主要講述以下內(nèi)容 async awiat 實(shí)質(zhì) async await 主要特性 async await 實(shí)質(zhì) 下面使用 pro...
摘要:現(xiàn)在不會(huì)用都不好意思說(shuō)自己是前端,為什么火起來(lái),一句話解決了回調(diào)嵌套和執(zhí)行順序問(wèn)題最重要的我感覺(jué)是解決順序問(wèn)題。 現(xiàn)在不會(huì)用Promise都不好意思說(shuō)自己是前端,Promise為什么火起來(lái),一句話解決了回調(diào)嵌套和執(zhí)行順序問(wèn)題最重要的我感覺(jué)是解決順序問(wèn)題。 不過(guò)開(kāi)始寫(xiě)之前我們先看看,promise怎么解決問(wèn)題,怎么用。列舉一個(gè)順序加載圖片demo: //需求 加載三張圖片 img1,im...
閱讀 2346·2021-11-23 09:51
閱讀 1152·2021-11-22 13:52
閱讀 3623·2021-11-10 11:35
閱讀 1203·2021-10-25 09:47
閱讀 3008·2021-09-07 09:58
閱讀 1073·2019-08-30 15:54
閱讀 2830·2019-08-29 14:21
閱讀 3041·2019-08-29 12:20