簡答題
一、談?wù)勀闶侨绾卫斫釰S異步編程的叙量,EcentLoop倡蝙、消息隊列都是做什么的,什么是宏任務(wù)绞佩,什么是微任務(wù)寺鸥?
- JS 異步編程:
- 解答:
? ? ? ?JavaScript語言的執(zhí)行環(huán)境是單線程,單線程是指一次只能完成一個任務(wù)品山,如果有多個任務(wù)胆建,則需要排隊,等待前一個任務(wù)完成后肘交,才能開始后一個任務(wù)笆载。基于這種原因而產(chǎn)生了兩種執(zhí)行任務(wù)的模式:同步模式和異步模式涯呻,且隨著JavaScript面臨的需求要來越多凉驻,它可以運行在瀏覽器、服務(wù)器上等复罐,為了滿足這些需求涝登,使得JavaScript的規(guī)模和復(fù)雜性也在持續(xù)增長,所以JavaScriptd中的異步編程也在不斷地調(diào)整市栗,往更友好的方向發(fā)展缀拭,JavaScript異步編程經(jīng)歷了回調(diào)函數(shù)咳短、Promise填帽、生成器函數(shù)Generator、以及現(xiàn)在Async/Await等幾個發(fā)展階段咙好。
? ? ? ?在第一階段使用的是回調(diào)函數(shù)篡腌,它是最基本的異步操作方式,以Ajax請求最為常見,它是最簡單勾效、容易理解和實現(xiàn)的異步編程嘹悼;但是不利于代碼的閱讀和維護,各部分之間高度耦合层宫,使得程序結(jié)構(gòu)混亂杨伙,流程難以追蹤,每個任務(wù)只能指定一個回調(diào)函數(shù)萌腿,不能使用try catch捕獲錯誤限匣,不能直接return,且極易出現(xiàn)回調(diào)地獄導(dǎo)致的調(diào)式困難毁菱,以及控制反轉(zhuǎn)導(dǎo)致的一系列信任問題
- 解答:
ajax(urlA, () => {
// 處理邏輯
ajax(urlB, () => {
// 處理邏輯
ajax(urlC, () => {
// 處理邏輯
})
})
})
? ? ? ?在第二階段引入了Promise米死,它是ES6推出的一種異步編程的解決方案锌历。Promise是承諾的意思,這個承諾在未來某個時刻會有一個確定的答復(fù)峦筒,該承諾有三種狀態(tài):等待中(pending)究西、完成(resolved)、拒絕(rejected)物喷。Promise是個構(gòu)造函數(shù)卤材,接受一個函數(shù)作為參數(shù)。作為參數(shù)的函數(shù)有兩個參數(shù):resolve和reject峦失,分別對應(yīng)完成和拒絕兩種狀態(tài)商膊。我們可以選擇在不同時候執(zhí)行resolve或reject去觸發(fā)下一個動作,執(zhí)行then方法里的函數(shù)宠进。Promise實現(xiàn)了鏈?zhǔn)秸{(diào)用晕拆,每次調(diào)用then之后返回的都是一個Promise對象,如果在then使用了return材蹬,return返回的值會被Promise.resolve()包裝实幕。它很好的解決了回調(diào)函數(shù)中的回調(diào)地獄,解決了控制反轉(zhuǎn)導(dǎo)致的信任問題堤器,將代碼的執(zhí)行主動權(quán)拿了回來昆庇;但是Promise一旦狀態(tài)從等待改變?yōu)槠渌麪顟B(tài)就不再可變了,還有如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯誤闸溃。
new Promise((resolve, reject) => {
console.log('new Promise');
resolve('success'); })
console.log('end');
? ? ? ?第三階段使用生成器函數(shù)Generator整吆,它是一種特殊的函數(shù),其最大的特點是控制函數(shù)的執(zhí)行辉川”眚可以讓我們用同步的方式寫代碼,可以分步執(zhí)行并得到異步操作的結(jié)果乓旗,能夠知曉異步操作的過程府蛇,以及切入修改異步操作的過程。但是需要手動去控制next(...)屿愚,將回調(diào)成功的返回數(shù)據(jù)送回JavaScript的主流程中汇跨;
function *foo(x) {
let y = 2 * (yield (x + 1));
let z = yield (y / 3);
return (x + y + z);
}
let it = foo(5);
console.log(it.next()); // => {value: 6, done: false}
console.log(it.next(12)); // => {value: 8, done: false}
console.log(it.next(13));// => {value: 42, done: true}
? ? ? ?最新階段的Async/Await異步處理編程,其中async函數(shù)返回一個 Promise 對象妆距,就是將函數(shù)返回使用Promise.resolve()穷遂,和then處理返回值一樣,可以使用then方法添加回調(diào)函數(shù)娱据。await后邊一般跟Promise對象蚪黑,async函數(shù)執(zhí)行遇到await后,等待后面的Promise對象的狀態(tài)從pending變成resolve后,將resolve的參數(shù)返回并自動往下執(zhí)行直到下一個await或結(jié)束祠锣。它解決了Generator需要手動控制next(...)執(zhí)行的問題酷窥。但它存在一個缺陷是如果多個異步代碼沒有依賴性卻使用了await會導(dǎo)致性能降低。
async function test() {
console.log('1')
}
console.log(test) // Promise {<resolve>: "1"}
? ? ? ?整個異步過程都是通過內(nèi)部的消息隊列和事件循環(huán)實現(xiàn)的
? ? ? ?每個階段的突破都是為了解決現(xiàn)有階段的技術(shù)問題伴网,異步編程的發(fā)展也是一個循序漸進的過程蓬推。
- EventLoop、消息隊列:
- 解答:
? ? ? ?事件循環(huán)機制和消息隊列的維護是由事件觸發(fā)線程控制的澡腾,事件觸發(fā)線程是由瀏覽器渲染引擎提供的沸伏,它會維護一個消息隊列
? ? ? ?EventLoop是事件循環(huán),主要負(fù)責(zé)監(jiān)聽調(diào)用棧(執(zhí)行棧)和消息隊列(任務(wù)隊列)动分,一旦調(diào)用棧中所有的任務(wù)都結(jié)束了毅糟,事件循環(huán)就會從消息隊列中取出第一個回調(diào)函數(shù),然后壓入到調(diào)用棧中澜公,一旦消息隊列中發(fā)生了變化姆另,事件循環(huán)就會監(jiān)聽到
? ? ? ?消息隊列:消息隊列是類似隊列的數(shù)據(jù)結(jié)構(gòu),遵循先入先出(FIFO)的規(guī)則坟乾。如果把調(diào)用棧理解為正在執(zhí)行的工作表迹辐,那么消息隊列則可以理解成待辦的工作表,JS引擎會先做完調(diào)用棧中所有的任務(wù)甚侣,然后通過事件循環(huán)從消息隊列中再取一個任務(wù)出來繼續(xù)執(zhí)行明吩,以此類推,整個過程隨時可以往消息隊列中再去放任務(wù)殷费,這些任務(wù)在消息隊列中會排隊等待事件循環(huán)
? ? ? ?事件循環(huán)機制:
? ? ? ? ? ?1.JS引擎線程會維護一個執(zhí)行棧印荔,同步代碼會依次加入執(zhí)行棧中,然后依次執(zhí)行并出棧
? ? ? ? ? ?2.JS引擎線程遇到異步函數(shù)详羡,會將異步函數(shù)交給相應(yīng)的WebApi,并繼續(xù)執(zhí)行后面的任務(wù)
? ? ? ? ? ?3.WebApi會在條件滿足的時候仍律,將異步對應(yīng)的回調(diào)加入到消息隊列中,等待執(zhí)行
? ? ? ? ? ?4.執(zhí)行棧為空時殷绍,JS引擎線程會去取消息隊列中的回調(diào)函數(shù)(如果有的話)染苛,并加入到執(zhí)行棧中執(zhí)行
? ? ? ? ? ?5.完成后出棧鹊漠,繼續(xù)執(zhí)行4的操作主到,直至消息隊列中的回調(diào)函數(shù)為空,以上便是事件循環(huán)的機制
- 解答:
- 宏任務(wù)躯概、微任務(wù)
- 解答:
? ? ? ?在JS中登钥,有兩類任務(wù)隊列:宏任務(wù)隊列(macrotask)和微任務(wù)隊列(microtask),宏任務(wù)可以由多個娶靡,微任務(wù)只有一個- macrotask:主代碼塊牧牢、setTimeout淆珊、setInterval艘蹋、setImmediate、I/O、UI rendering等(可以看到慢洋,事件隊列中的每一個事件都是一個 macrotask,現(xiàn)在稱之為宏任務(wù)隊列)
- microtask:Promise洲拇、process.nextTick洪添、Object.observer等
- 解答:
? ? ? ?每次執(zhí)行棧執(zhí)行的代碼即是一個宏任務(wù),包括任務(wù)隊列(宏任務(wù)隊列)中的掌唾,因為執(zhí)行棧中的宏任務(wù)執(zhí)行完后會去取任務(wù)隊列(宏任務(wù)隊列)中的任務(wù)加入執(zhí)行棧中
? ? ? ?在執(zhí)行宏任務(wù)時遇到Promise等放前,會創(chuàng)建微任務(wù)(.then()里面的回調(diào)),并加入到微任務(wù)隊列隊尾糯彬;微任務(wù)必然是在某個宏任務(wù)執(zhí)行的時候創(chuàng)建的凭语,而在下一個宏任務(wù)開始之前,瀏覽器會對頁面重新渲染撩扒。同時似扔,在上一個宏任務(wù)執(zhí)行完成后,渲染頁面之前搓谆,會執(zhí)行當(dāng)前微任務(wù)隊列中的所有微任務(wù)虫几。
? ? ? ?執(zhí)行機制
? ? ? ? ? ?1.執(zhí)行一個宏任務(wù)(執(zhí)行棧中沒有就從消息隊列中獲取)
? ? ? ? ? ?2.執(zhí)行過程中如果遇到微任務(wù)挽拔,就將微任務(wù)添加刀微任務(wù)的任務(wù)隊列中
? ? ? ? ? ?3.宏任務(wù)執(zhí)行完畢后辆脸,立即執(zhí)行當(dāng)前微任務(wù)隊列中的所有微任務(wù)(依次執(zhí)行)
? ? ? ? ? ?4.當(dāng)前宏任務(wù)執(zhí)行完畢,開始檢查渲染螃诅,然后GUI接管渲染
? ? ? ? ? ?5.喧渲染完畢后啡氢,JS引擎線程繼續(xù),開始下一個宏任務(wù)(從宏任務(wù)隊列中獲仁趼恪)
代碼題
一倘是、將下面異步代碼使用Promise的方式改進
解答:使用Promise的方式改進實現(xiàn)如下:
// 使用Promise改進setTimeout異步代碼
new Promise((resolve, reject) => {
resolve('hello')
}).then(value => {
return value + ' ' + 'lagou' // value = hello
})
.then(value => {
return value + ' ' + 'I ? U' // value = hello lagou
})
.then(value => {
console.log(value) // hello lagou I ? U
})
二、基于以下代碼完成四個練習(xí)
練習(xí)1:使用組合函數(shù)fp.flowRight()重新實現(xiàn)下面這個函數(shù)
解答:fp.flowRight()重新實現(xiàn)如下:
//練習(xí)1
let isLastInstock = fp.flowRight(fp.prop('in_stock') , fp.last)
console.log(isLastInstock(cars)) //false 即最后一條數(shù)據(jù)的in_stock屬性值為fase
練習(xí)2:使用fp.flowRight()袭艺,fp.prop()搀崭,fp.first()獲取第一個car的name
解答:獲取第一個car的name實現(xiàn)如下:
// 練習(xí)2
let getFirstCarName = fp.flowRight(fp.props('name'), fp.first)
console.log(getFirstCarName(cars)) //[ 'Ferrari FF' ]
練習(xí)3:使用幫助函數(shù)_average()重構(gòu)averageDollarValue,使用函數(shù)組合方式實現(xiàn)
解答:重構(gòu)averageDollarValue實現(xiàn)如下:
let averageDollarValue = fp.flowRight(_average,fp.map(car => car.dollar_value))
console.log(averageDollarValue(cars)) //790700
練習(xí)4:使用flowRight寫一個sanitizeName()函數(shù),返回一個下劃線連接的小寫字符串猾编,把數(shù)組中的name轉(zhuǎn)換為這種形式瘤睹,例如:sanitizeName(["Hello World"]) => ["hello_world"]
解答:使用flowRight寫一個sanitizeName()函數(shù)實現(xiàn)如下:
// 練習(xí)4
let _underscore = fp.replace(/\W+/g, '_')
//先遍歷數(shù)組把里面字符串轉(zhuǎn)成小寫,在遍歷數(shù)組將非單字字符轉(zhuǎn)換成下劃線
let sanitizeName1 = fp.flowRight( fp.map(_underscore), fp.map(fp.toLower))
//遍歷數(shù)組中使用fp.flowRight組合函數(shù)答倡,先轉(zhuǎn)換成小寫轰传,再將非單字字符轉(zhuǎn)換成下劃線
let sanitizeName2 = fp.flowRight( fp.map(fp.flowRight(_underscore, fp.toLower)))
console.log(sanitizeName2(["Hello World",'LaGou Study'])) //[ 'hello_world', 'lagou_study' ]
三、基于下面提供的四個代碼瘪撇,完成后續(xù)的四個練習(xí)
練習(xí)1:使用fp.add(x,y)和fp.map(f,x)創(chuàng)建一個能讓functor里的值增加的函數(shù)ex1
解答:讓functor里的值增加的函數(shù)實現(xiàn)如下:
// 練習(xí)1
let ex1 = () => {
return fp.map(fp.add(1), maybe.map(x => x)._value)
}
console.log(ex1()) //[ 6, 7, 2 ]
練習(xí)2:實現(xiàn)一個函數(shù)ex2获茬,能夠fp.first獲取列表的第一個元素
解答:獲取列表的第一個元素實現(xiàn)如下:
//練習(xí)2
let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do'])
let ex2 = () => {return fp.first(xs.map(x => x)._value)}
console.log(ex2()) //do
練習(xí)3:實現(xiàn)一個函數(shù)ex3港庄,使用safeProp和fp.first找到user的名字的首字母
解答:使用safeProp和fp.first找到user的名字的首字母實現(xiàn)如下:
// 練習(xí)3
let safeProp = fp.curry(function (x, o){
return Maybe.of(o[x])
})
let user = {id: 2, name: 'Albert'}
let ex3 = () => {
return fp.first(safeProp(Object.keys(user).indexOf('name'),Object.values(user))._value)
}
console.log(ex3()) //A
練習(xí)4:使用MayBe重寫ex4,不要有if語句
// 練習(xí)4
let ex4 = function (n) {
if(n){
return(parseInt(n))
}
}
let ex5 = n =>{
return Maybe.of(n)._value
}
let ex6 = n =>{
return !!n? Maybe.of(n)._value : undefined
}
console.log(ex4(''))
console.log(ex5(''))
console.log(ex6(''))
注:ex5的寫法會在空字符串和null的時候?qū)е螺敵龊蚭x4,ex6不一樣
四恕曲、手寫實現(xiàn)MyPromise源碼
要求:盡可能還原Promise中的每一個API鹏氧,并通過注釋的方式描述思路和原理
MyPromise源碼(myPromise.js)
/*由于這個狀態(tài)頻繁使用,為了使用這個常量時編輯器有代碼提示并能夠復(fù)用佩谣,故把它定義為常量*/
const PENDING = 'pending' //等待
const FULFILLED = 'fulfilled' //成功
const REJECTED = 'rejected' //失敗
class MyPromise{
//構(gòu)造函數(shù) ==》 立即執(zhí)行執(zhí)行器 ==》 指的是傳遞過來的回調(diào)函數(shù)
constructor(executor) {
// 執(zhí)行器錯誤處理 當(dāng)執(zhí)行器中代碼在執(zhí)行過程中發(fā)生錯誤的時候度帮,這個時候就讓promise狀態(tài)變成失敗
try{
executor(this.resolve, this.reject)
}catch (e) {
//捕獲執(zhí)行器的錯誤
this.reject(e)
}
}
/*狀態(tài)是每個promise對象獨有的,故因該把狀態(tài)屬性定義為實例屬性*/
status = PENDING //promise狀態(tài) ===》 默認(rèn)值為等待
//由于每個promise對象都有自己成功之后的值稿存,都有自己失敗的原因笨篷,故應(yīng)該把這兩個屬性定義為實例的屬性
value = undefined //成功之后的值
reason = undefined //失敗后的原因
//由于then方法可能會被多次調(diào)用,聯(lián)想到數(shù)組能夠同時存儲多個函數(shù) 故在這里將屬性定義為數(shù)組
successCallback = [] //成功回調(diào)
failCallback = [] //失敗回調(diào)
//定義為箭頭函數(shù)的目的:將來在直接調(diào)用某個普通函數(shù)時瓣履,這個函數(shù)的this指向是window或者undefined,為了能讓this的指向為類的實例對象即promise對象率翅,故而使用箭頭函數(shù)
resolve = value => {
//為了確保狀態(tài)確定后就不可更改,需加以判斷
//如果狀態(tài)不是等待袖迎,阻止程序向下執(zhí)行
if(this.status !== PENDING) return
//將狀態(tài)更改為成功
this.status = FULFILLED
//將傳遞過來的值進行賦值 ==》 保存成功后的值
this.value = value
//判斷成功回調(diào)是否存在 如果存在 則調(diào)用并傳遞相應(yīng)的參數(shù)
//數(shù)組中存儲了多個回調(diào)函數(shù)冕臭,我們需要循環(huán)遍歷這個數(shù)組,并在循環(huán)的過程中調(diào)用這個回調(diào)函數(shù)
//當(dāng)數(shù)組的長度不為0時燕锥,就繼續(xù)執(zhí)行循環(huán)體中的代碼
//考慮到需要從前往后執(zhí)行辜贵,故調(diào)用數(shù)組的shift方法,把前面的回調(diào)函數(shù)彈出來
while(this.successCallback.length) this.successCallback.shift()()
}
reject = reason => {
//為了確保狀態(tài)確定后就不可更改归形,需加以判斷
//如果狀態(tài)不是等待托慨,阻止程序向下執(zhí)行
if(this.status !== PENDING) return
//將狀態(tài)更改為失敗
this.status = REJECTED
//將傳遞過來的值進行賦值 ==》 失敗后的原因
this.reason = reason
//判斷失敗回調(diào)是否存在 如果存在 則調(diào)用并傳遞相應(yīng)的參數(shù)
//數(shù)組中存儲了多個回調(diào)函數(shù),我們需要循環(huán)遍歷這個數(shù)組暇榴,并在循環(huán)的過程中調(diào)用這個回調(diào)函數(shù)
//當(dāng)數(shù)組的長度不為0時厚棵,就繼續(xù)執(zhí)行循環(huán)體中的代碼
//考慮到需要從前往后執(zhí)行,故調(diào)用數(shù)組的shift方法蔼紧,把前面的回調(diào)函數(shù)彈出來
while(this.failCallback.length) this.failCallback.shift()()
}
//then要被定義在原型對象中婆硬,并接受成功回調(diào)和失敗回調(diào)兩個參數(shù)
then (successCallback, failCallback){
//在調(diào)用then方法不傳遞任何參數(shù)的時候,要將最先的成功狀態(tài)依次傳遞給有回調(diào)函數(shù)的then方法
successCallback = successCallback? successCallback : value => value
failCallback = failCallback? failCallback : reason => {throw reason}
//then要實現(xiàn)鏈?zhǔn)秸{(diào)用奸例,then方法必須返回promise對象彬犯,上一個回調(diào)的返回值傳遞給下一個then成功的回調(diào)
let promise2 = new MyPromise((resolve, reject)=>{
//判斷狀態(tài)并調(diào)用對應(yīng)的回調(diào)函數(shù)并傳遞相應(yīng)的參數(shù)
if(this.status === FULFILLED){
//由于promise2還沒被實例完,在實例過程中獲取不到查吊,故使用異步實現(xiàn)谐区,讓同步代碼先實現(xiàn),再來執(zhí)行異步代碼菩貌,這時候就能獲取到promise2
setTimeout(()=>{
//then方法中的回調(diào)函數(shù)在執(zhí)行過程中報錯卢佣,這個錯誤要在下一個then的reject中捕獲到
try{
let x = successCallback(this.value)
resolvePromise( promise2, x, resolve, reject)
}catch (e) {
//捕獲then回調(diào)的錯誤
reject(e)
}
}, 0)
}else if(this.status ===REJECTED){
//由于promise2還沒被實例完,在實例過程中獲取不到箭阶,故使用異步實現(xiàn)虚茶,讓同步代碼先實現(xiàn),再來執(zhí)行異步代碼仇参,這時候就能獲取到promise2
setTimeout(()=>{
// then方法中的回調(diào)函數(shù)在執(zhí)行過程中報錯嘹叫,這個錯誤要在下一個then的reject中捕獲到
try{
let x = failCallback(this.reason)
resolvePromise( promise2, x, resolve, reject)
}catch (e) {
//捕獲then回調(diào)的錯誤
reject(e)
}
}, 0)
}else{
//說明當(dāng)前狀態(tài)等待,但不知是調(diào)用成功還是失敗的回調(diào)
//故將成功回調(diào)和是失敗回調(diào)存儲起來
this.successCallback.push(() => {
successCallback()
setTimeout(()=>{
try{
let x = successCallback(this.value)
resolvePromise( promise2, x, resolve, reject)
}catch (e) {
//捕獲then回調(diào)的錯誤
reject(e)
}
}, 0)
})
this.failCallback.push(() => {
setTimeout(()=>{
try{
let x = failCallback(this.reason)
resolvePromise( promise2, x, resolve, reject)
}catch (e) {
//捕獲then回調(diào)的錯誤
reject(e)
}
}, 0)
})
}
});
return promise2
}
//無論當(dāng)前這個promise對象最終的狀態(tài)是成功還是失敗诈乒,finally中的這個回調(diào)函數(shù)始終都會執(zhí)行一次e
//在finally方法的后面可以鏈?zhǔn)降恼{(diào)用then方法拿到當(dāng)前這個promise對象返回的結(jié)果
finally (callBack){
//如何得到當(dāng)前promise對象的狀態(tài) ==》 this.then方法可以
return this.then(value=>{
//在finally中return了一個promise對象
return MyPromise.resolve(callBack()).then(()=>value)
},reason=>{
return MyPromise.resolve(callBack()).then(()=> {throw reason})
})
}
//用于當(dāng)前promise對象失敗的情況罩扇,如果沒有傳遞失敗的回調(diào)函數(shù),則會被catch捕獲到
catch(failCallback){
return this.then(undefined, failCallback)
}
// all方法是用來解決異步并發(fā)問題的怕磨,它允許我們按照異步代碼調(diào)用的順序得到異步執(zhí)行的結(jié)果
// all的返回值也是一個promise對象喂饥,在后面可以鏈?zhǔn)綄τ胻hen方法
// 在all中的promise對象都是成功的,那么all最后的結(jié)果也是成功肠鲫,如果有一個失敗员帮,那么結(jié)果就是失敗的
// 通過類調(diào)用,故為靜態(tài)方法,該方法接收一個數(shù)組作為參數(shù)
static all (array){
let results = []
let index = 0
return new MyPromise((resolve, reject) => {
function addData(key, value) {
results[key] = value
index++
// 針對異步情況下导饲,判斷index的值和數(shù)組的長度相等捞高,說明數(shù)組中所有的內(nèi)容都執(zhí)行完了,從而而決定是否調(diào)用resolve回調(diào)函數(shù)
if(index === array.length){
resolve(results)
}
}
//循環(huán)該數(shù)組
for(let i = 0; i < array.length; i++){
let current = array[i]
if(current instanceof MyPromise){
//promise對象渣锦,若成功的話將成功的值添加到數(shù)組中硝岗,若有一個失敗的話失敗的話則將失敗的原因通過reject回調(diào)
current.then(value => addData(i, value), reason => reject(reason))
}else{
//普通值 則直接添加到數(shù)組中
addData(i, array[i])
}
}
})
}
//如果是普通值,則創(chuàng)建一個promise對象袋毙,被將這個值包裹在promise對象中型檀,然后把創(chuàng)建出來的promise對象作為resolve方法的返回值,才能在后面鏈?zhǔn)秸{(diào)用then方法
//如果是promise對象听盖,則原封不動的將這個promise對象再作為resolve方法的返回值贱除,所以才能在后面調(diào)用then方法,通過then方法的成功回調(diào)拿到這個promise對象的返回值
static resolve (value){
if(value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
}
//判斷 x 值時普通值還是promise對象
//如果是普通值媳溺,直接調(diào)用resolve
//如果是promise對象 查看promise對象返回的結(jié)果
//再根據(jù)promise對象返回的結(jié)果 決定調(diào)用resolve 還是調(diào)用reject
function resolvePromise(promise2, x, resolve, reject){
//相等表示自己返回自己(Promise對象自返回)月幌,這時應(yīng)該調(diào)用reject回調(diào)函數(shù)
if(promise2 === x){
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if( x instanceof MyPromise){
//promise對象
x.then(resolve,reject)
}else{
//普通值
resolve(x)
}
}
//導(dǎo)出當(dāng)前類
module.exports = MyPromise;
測試代碼(index.js)
/**
* 了解promise原理及原理代碼實現(xiàn)
* 1.Promise 就是一個類 在執(zhí)行這個類的時候 需要傳遞一個執(zhí)行器進去 執(zhí)行器會立即執(zhí)行 故放在promise類的構(gòu)造器中
* 2.Promise 中有三種狀態(tài) 分別為 成功(fulfilled) 失敗(rejected) 等待(pending)
* pending -> fulfilled
* pending -> rejected
** 一旦狀態(tài)確定就不可更改
* 3.resolve和reject函數(shù)是用來更改狀態(tài)
* resolve:fulfilled
* reject:rejected
* 4.then方法內(nèi)部做的事情就是判斷狀態(tài) 如果狀態(tài)成功 調(diào)用成功的回調(diào)函數(shù) 如果狀態(tài)失敗 調(diào)用失敗的回調(diào)函數(shù) then被定義為原型對象當(dāng)中
* 5.then成功回調(diào)有一個參數(shù) 表示成功之后的值 then失敗回調(diào)有一個參數(shù) 表示失敗的原因
**/
//導(dǎo)向MyPromise
const MyPromise = require('./myPromise')
//創(chuàng)建promise對象 resolve reject 兩個函數(shù)參數(shù)
let promise = new Promise((resolve, reject) => {
//把狀態(tài)變成成功 參數(shù)為成功后的值
resolve()
//把狀態(tài)編程失敗 參數(shù)為失敗的原因
reject()
})
//需實現(xiàn)promise的then方法,要先了解其中傳遞兩個回調(diào)函數(shù)及其含義
//調(diào)用then方法時悬蔽,首先要去判斷promise狀態(tài)扯躺,成功的話則調(diào)用成功回調(diào)函數(shù),失敗的話則調(diào)用失敗回調(diào)函數(shù)
//由于then能被任意一個promise對象調(diào)用蝎困,故應(yīng)該將它定義在原型對象中
//then成功回調(diào)有一個參數(shù)(value) 表示成功之后的值 then失敗回調(diào)有一個參數(shù)(reason) 表示失敗的原因
promise.then(value=>{},reason=>{})
/** 1.類核心邏輯實現(xiàn)調(diào)用 **/
/*let promise1 = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失敗')
})
promise1.then(value => {
console.log(value) // 成功
}, reason => {
console.log(reason) // 失敗
})*/
/** 2.在Promise類中加入異步邏輯 **/
/*let promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
},2000)
// reject('失敗')
})
promise2.then(value => {
console.log(value) // 等待2s后輸出成功
}, reason => {
console.log(reason) // 等待2s后輸出失敗
})*/
/** 3.實現(xiàn)then方法多次調(diào)用添加多個處理函數(shù) **/
/*let promise3 = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失敗')
})
promise3.then(value => {
console.log(1) // 1
console.log(value) // 成功
}, reason => {
console.log(reason) // 失敗
})
promise3.then(value => {
console.log(2) // 2
console.log(value) // 成功
}, reason => {
console.log(reason) // 失敗
})
promise3.then(value => {
console.log(3) // 3
console.log(value) // 成功
}, reason => {
console.log(reason) // 失敗
})*/
/** 4./5.實現(xiàn)then方法的鏈?zhǔn)秸{(diào)用 **/
//后面then方法拿到的值實際上是上一個回調(diào)函數(shù)的返回值
/*let promise4 = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失敗')
})
//測試4 then中返回值的情況
promise4.then(value => {
console.log(value) // 成功
return 100
}).then(value => {
console.log(value)
})
//測試5 then中返回promise情況
function other(){
return new MyPromise((resolve, reject) => {
resolve('other')
})
}
promise4.then(value => {
console.log(value) // 成功
return other()
}).then(value => {
console.log(value) // other
})*/
/** 6.then方法鏈?zhǔn)秸{(diào)用識別Promise對象自返回 **/
/*let promise6 = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失敗')
})
let p6 = promise6.then(value => {
console.log(value)
return p6
})
p6.then(value => {
console.log(value) //成功
},reason => {
console.log(reason.message) //Chaining cycle detected for promise #<Promise>
})*/
/** 7.捕獲錯誤及then鏈?zhǔn)秸{(diào)用其他狀態(tài)代碼補充 **/
// 執(zhí)行器錯誤 then方法中的回調(diào)函數(shù)在執(zhí)行過程中報錯录语,這個錯誤要在下一個then的reject中捕獲到
/*let promise7 = new MyPromise((resolve, reject) => {
// throw new Error('executor error')
// resolve('成功')
reject('失敗')
})
promise7.then(value => {
console.log(value) //成功
},reason => {
console.log(reason.message) //executor error
})
promise7.then(value => {
console.log(value) //成功
throw new Error('then error')
},reason => {
console.log(reason.message) //executor error
return 10000
}).then(value => {
console.log(value) // 成功 10000
// throw new Error('then error')
},reason => {
console.log('xxx') //xxx
console.log(reason.message) //then error
})*/
/** 8.then中方法編程可選參數(shù) **/
/*let promise8 = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失敗')
})
promise8.then().then().then(value =>
console.log(value) //成功 失敗
)*/
function p1() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 2000)
})
}
function p2() {
return new MyPromise((resolve, reject) => {
// resolve('p2')
reject('失敗')
})
}
/** 9.promise.all方法的實現(xiàn) **/
/*MyPromise.all(['a', 'b', p1(), p2(), 'c']).then(result =>
console.log(result) // [ 'a', 'b', 'p1', 'p2', 'c' ]
)*/
/** 10.promise.resolve方法的實現(xiàn) **/
/*MyPromise.resolve(100).then(value => console.log(value))
MyPromise.resolve(p1()).then(value => console.log(value))*/
/** 10.promise.finally方法的實現(xiàn) **/
/*p2().finally(()=>{
console.log('finally')
return p1()
}).then(value=>{
console.log(value) // p2
},reason=>{
console.log(reason)
})*/
/** 11.promise.catch方法的實現(xiàn) **/
/*p2().then(value => console.log(value))
.catch(reason => console.log(reason))*/