談一下promise的使用(只談使用,不談機制)

只談使用,不談實現(新手向)

promise的認識:

相信你們對promise都有本身的見解,或者說實踐經歷,也可能還有些許朋友正在學習前端,恰好碰見了promise,想要找尋一下答案,我寫這篇文章呢,也不是多麼的詳解promise這個機制,更多的是想對剛接觸的朋友起到一個引導的做用,另外也是本身對自我認識的一個總結。前端

promise究竟是什麼

其實說直白一點,promise是現階段解決異步問題的 終極 方案,針對棘手的回調地獄咱們每每不只是頭疼,並且也不利於函數式編程,畢竟回調的寫法着實不如同步的寫法那樣符合直覺。編程

先放一段回調地獄,這是我作過的uni-app的一個項目,同事用到了回調的方法 :
function foo(res) {
	let self = this
	if (this.nick.length !== 0) {
		uni.request({//第一次異步請求
			url: 'https://........',
			data: {
				userid: self.$store.state.userInfo.ID,
				faceurl: self.$store.state.userInfo.FaceURL,
				nick: self.nick
			},
			success:function(res){
				uni.request({//第二次異步請求
					url: 'https://........',
					data: {
						userId: self.$store.state.userInfo.ID
					},
					success:function(res){
						if (res.data.IsSuccess) {
							self.$store.commit('refresh', res.data.User)
						}
					}
				})
				setTimeout( function () {
					uni.navigateBack({
						delta: 1
					})
				}, 1200)
			},
			complete: function (res) {
				self.$api.msg(res.data.Msg)
			}
		})
	}
}
複製代碼

這樣一系列回調下來,若是再須要發送一個請求的話,或者作一個其餘的異步操做,這樣嵌套層次就會很深,每一步異步操做都須要加在上一步的回調後面,而且這種寫法不太利於函數式編程,不管是對於維護仍是追加內容都不很友好,整個代碼結構像是 > 這種形狀,那麼下面來看看,一個promise的操做api

這是我寫的第三方登陸的一個方法,裏面有不少步異步操做,可是用promise寫下來,整個流程很清晰,感受就像是寫同步同樣 :
function logIn(provider){
	let self = this
	if(provider==='weixin' || provider==='sinaweibo'){
		uni.getProvider({//獲取uniapp支持的第三方數據(第一次異步)
			service:'oauth'
		}).then(data=>{
			var [err,res] = data
			var providers=res.provider
			var flagIndex=providers.indexOf(provider)
			if(flagIndex>-1){
				return providers[flagIndex]
			}
		}).then(res=>{
			return uni.login({//登錄接口(第二次異步)
				provider:res,
				scopes:'auth_base',
				timeout:20000,
			})
		}).then(data=>{
			var [err,res] = data
			if(res.errMsg==="login:ok"){
				self.authResult=res.authResult
				return res.authResult
			}
		}).then(res=>{
			return uni.getUserInfo({//獲取用戶的信息(第三次異步)
				provider:provider,
				timeout:20000,
				withCredentials:true
			})
		}).then(data=>{
			var [err,res] = data
			if(res.errMsg==="getUserInfo:ok"){
				return res
			}
		}).then(res=>{
			if(provider==='weixin')	
				self.getIsBindData.openid=res.userInfo.unionId
				self.getDataWX.nickname=res.userInfo.nickName
				self.getDataWX.headimgurl=res.userInfo.avatarUrl
				self.getDataWX.openid=res.userInfo.unionId
				self.getDataWX.unionid=res.userInfo.openId
				return self.$Request.get(self.isbindUrl,self.getIsBindData,false)//發送請求判斷是否綁定手機號(第四次異步)
			}else if(provider==='sinaweibo'){//若是綁定的是微博
				
			}
		}).then(res=>{
			if(provider==='weixin'){//若是是微信
				if(res.Data.isBind){//若是綁定了
					this.$store.commit('login', res.Data.user)
					uni.switchTab({//跳去首頁
						url: '/pages/index/index'
					})
				}else if(!res.Data.isBind){//若是沒綁定
					console.log(1)
					uni.navigateTo({
						url:`............`,
					})
				}
			}else{
				
			}
		})
	}
} 
複製代碼

用promise的寫法有木有很眼熟,像不像jq啊,這種鏈式調用就算有多步的異步函數寫出來的代碼依然很清晰,整個代碼就夠就像是 | ,一步一步接着來,似魔鬼的步伐promise

好了,說了這麼多,咱們就開始今日的promise之旅吧

使用場景

咱們模擬這樣一個場景微信

function foo(num){
        setTimeout(()=>{
            return num+100
        },1000)
    };
    foo(0);
複製代碼

請問各位看官,這個100能夠在1秒後能被拿到嘛?答案是不能拿到,由於咱們在1秒後已是異步的事情了,但如今有這樣的需求,咱們恰恰就是須要num+100來作後面的操做。app

function bar (num){
       console.log(num-10)
        return num-10
   }
複製代碼

咱們須要在foo以後獲得的數據放入bar中在進行操做,換成往常的作法即是:異步

function foo(num){
        setTimeout(()=>{
            let res = num+100
            bar(res)
        },1000)
    };
    foo(100);
複製代碼

那麼在1秒後,咱們確實能夠打印出190,可是若是十次,二十次,等等等這樣的異步操做呢?每一步都須要上一步結算獲得的數值進行下一步計算,整個代碼會顯得很冗餘。ide

function foo(num,fn){
        setTimeout(()=>{
             fn(num+100)
        },1000)
    };
複製代碼

那麼咱們在調用的時候就須要寫成foo(100,bar) 若是嵌套bar函數也是個異步,而且還須要嵌套其餘函數,這樣下來真的時候頭疼,煩都煩死了。可是用咱們的promise就能夠輕鬆搞定!!!!函數式編程

先來看看代碼

首先把foo重寫一下函數

function foo(num){
    return new Promise((res,rej)=>{//res,rej寫成啥都無所謂
        setTimeout(()=>{
            res(num+100)
        },1000)
    })
}
複製代碼

bar代碼不變,咱們調用的時候就能夠這麼來了

foo(100).then(res=>{
    bar(res)
})
複製代碼

一樣的會在1秒後繼續打印出來190,並且若是要將這個值繼續傳遞也很方便。

function foo(num){
        return new Promise((res,rej)=>{//res,rej寫成啥都無所謂
            setTimeout(()=>{
                res(num+100)
            },1000)
        })
    };
    function bar (num){
        console.log(num-10);
        return num-10
    };


    foo(100).then(res=>{
        return bar(res)
    }).then(res=>{
        return bar(res)
    }).then(res=>{
        return bar(res)
    }).then(res=>{
        console.log('能夠繼續傳遞下去哦'+res)
        return res
    })
複製代碼

各位在控制檯上打印一下這個數據,看看是否是這樣的:

神奇不神奇,只要你願意,這個函數能夠無限的鏈式調用下去,這樣不只讀起來舒服,寫起來也很舒服啊,改起來舒服。 各位應該看見了這個神奇的 .then 因此咱們立刻就要進入到真正的環節中。


promise到底應該怎麼去用

promise怎麼寫的

首先實例化一個promise對象

let promise_a = new Promise((resolve,reject)=>{console.log('...code')})
複製代碼

new Promise()中接受一個參數 該參數是一個函數 我習慣性的寫成了箭頭函數,不懂箭頭函數的朋友能夠再去學習一下。 這個函數中接收兩個參數 分別是:resolve,reject 其中resolve,reject只是兩個形參,一個表明成功,一個表明失敗,不在意這倆寫成啥,寫成這樣foo,bar 也無所謂 eg :let promise_a = new Promise((foo,bar)=>{console.log('...code')}) 同樣的

那麼咱們姑且認爲這個resolve就像是function中的return同樣,reject就相似throw同樣。 在一個函數中,promise到底應該這麼用呢。 看代碼:

function foo_(num){
        return new Promise((res,rej)=>{//首先new一個promise 這個promise實例是必需要返回(return)的,否則將沒法將這個數據吐出去
            setTimeout(()=>{
                try{
                    const a = num+1
                    //a = 1 
                    res(a)//將正確的數據返出去 能夠用.then接
                }catch(e){
                    rej(e)//將錯誤的數據返出去 能夠用.then接
                }
            })
        })
    };
複製代碼

以上代碼 若是a=1被註釋,那麼成功的數據將被res(a)拋出去,若是不被註釋,那麼就報錯,就會被rej(e)所有拋出去。

拋出去的數據將會被.then接住


神奇的.then

.then 是實例化的promise對象的一個方法。.then() 接受兩個參數,成功的回調函數,失敗的回調函數即:

foo_(100).then(
        success=>{//成功的回調
            console.log(`success,成功的回調:${success}`)
        },
        error=>{//失敗的回調
            console.log(`error,失敗的回調:${error}`)
        }
    )
複製代碼

以上面的foo_爲例 首先咱們把 a = 1 註釋掉

//首先咱們把 a = 1 註釋掉 
    foo_(100).then(
        success=>{//成功的回調
            console.log(`success,成功的回調:${success}`)
        },
        error=>{//失敗的回調
            console.log(`error,失敗的回調:${error}`)
        }
    )
複製代碼

打印結果以下:

在以上代碼中,success 其實就是foo_中res(a) 中的a 就像我上文所說,相似return同樣,是能拋出去,而且能被接住。只是能在.then中接到。

如今 咱們將 // a = 1 的註釋去掉,一樣調用上方的代碼: 首先咱們把 a = 1 去掉註釋!!

//首先咱們把 a = 1 去掉註釋!!
    foo_(100).then(
        success=>{//成功的回調
            console.log(`success,成功的回調:${success}`)
        },
        error=>{//失敗的回調
            console.log(`error,失敗的回調:${error}`)
        }
    )
複製代碼

打印結果以下:

在以上代碼中,error 其實就是foo_中rej(e) 中的e 就像我上文所說,相似 throw同樣,是能拋出去,而且能被接住。只是能在 .then中接到。


.then 如何傳遞下一個數據

以上方代碼爲例
如今,咱們再添加一個函數
同步函數:
function baz(num){
    return num+10
}
咱們想在
foo_(100).then(
        success=>{//成功的回調
            console.log(`success,成功的回調:${success}`)
        },
        error=>{//失敗的回調
            console.log(`error,失敗的回調:${error}`)
        }
    )
以後再將 success 或者 error 進行操做而且在傳遞下去
那麼使用return 就好

咱們先把 foo_ 中的 a = 1 註釋掉

function foo_(num){
        return new Promise((res,rej)=>{//首先new一個promise 這個promise實例是必需要返回(return)的,否則將沒法將這個數據吐出去
            setTimeout(()=>{
                try{
                    const a = num+1
                    //a = 1 
                    res(a)//將正確的數據返出去 能夠用.then接
                }catch(e){
                    rej(e)//將錯誤的數據返出去 能夠用.then接
                }
            })
        })
    };


在控制檯上打出這段代碼
foo_(100).then(
        success=>{//成功的回調
            let result = success+10
            console.log(`success,成功的回調:${result}`)
            return result
        },
        error=>{//失敗的回調
            let err = error
            console.log(`error,失敗的回調:${err}`)
            return error
        }
    ).then(
        success=>{
            let res = baz(success)
            console.log(`res=${res}`)
        },
        error=>{
            console.log(`這是一個錯誤:${error}`)
        }
    )
複製代碼

打印結果:

由於是全是成功的操做,因此天然都走了成功的結果。

咱們把 foo_ 中的 a = 1 去掉註釋
    
    function foo_(num){
        return new Promise((res,rej)=>{//首先new一個promise 這個promise實例是必需要返回(return)的,否則將沒法將這個數據吐出去
            setTimeout(()=>{
                try{
                    const a = num+1
                    a = 1 
                    res(a)//將正確的數據返出去 能夠用.then接
                }catch(e){
                    rej(e)//將錯誤的數據返出去 能夠用.then接
                }
            })
        })
    };
    在控制檯上打出這段代碼
foo_(100).then(
        success=>{//成功的回調
            let result = success+10
            console.log(`success,成功的回調:${result}`)
            return result
        },
        error=>{//失敗的回調
            let err = error
            console.log(`error,失敗的回調:${err}`)
            return error
        }
    ).then(
        success=>{
            let res = baz(success)
            console.log(`res=${res}`)
        },
        error=>{
            console.log(`這是一個錯誤:${error}`)
        }
    )
複製代碼

打印結果:

你們是否是以爲很詫異,咦,怎麼沒有進入到 console.log(`這是一個錯誤:${error}`)這一句 反而進入了 console.log(`res=${res}`)中去呢。

好的,問題出來了,咱們繼續講.then 其實在每一次.then 的時候 不管是進入成功的回調,即:

success=>{
            let res = baz(success)
            console.log(`res=${res}`)
        },
複製代碼

仍是失敗的回調,即:

error=>{
            console.log(`這是一個錯誤:${error}`)
        }
複製代碼

都是能夠認爲是一個獨立的 promise , 其後方的 .then 關注的是上一個的處理結果,

因此在處理這個問題的時候, .then以後的兩個藍色箭頭所指的函數僅僅只處理黃色成功箭頭函數以後的東西,由於根本就沒有走到與黃色箭頭的並列的error函數中去

而針對去掉 a = 1 的註釋以後 發生的事情,如圖所示

因此若是我想要函數可以進入到

error=>{
            console.log(`這是一個錯誤:${error}`)
        }
複製代碼

就必須不管在

success=>{//成功的回調
            let result = success+10
            console.log(`success,成功的回調:${result}`)
            return result
        },
        error=>{//失敗的回調
            let err = error
            console.log(`error,失敗的回調:${err}`)
            return error
        }
複製代碼

中拋出一個錯誤 上代碼:

function foo_(num){
        return new Promise((res,rej)=>{//首先new一個promise 這個promise實例是必需要返回(return)的,否則將沒法將這個數據吐出去
            setTimeout(()=>{
                try{
                    const a = num+1
                    //a = 1 
                    res(a)//將正確的數據返出去 能夠用.then接
                }catch(e){
                    rej(e)//將錯誤的數據返出去 能夠用.then接
                }
            })
        })
    };
    
    
    foo_(100).then(
        success=>{//成功的回調 
            let result = success+10
            console.log(`success,成功的回調:${result}`)
            throw new Error( result)
        },
        error=>{//失敗的回調
            let err = error
            console.log(`error,失敗的回調:${err}`)
            return error
        }
    ).then(
        success=>{
            let res = baz(success)
            console.log(`res=${res}`)
        },
        error=>{
            console.log(`這是一個錯誤:${error}`)
        }
    )
複製代碼

若是咱們這麼寫呢?

function foo_(num){
        return new Promise((res,rej)=>{
            setTimeout(()=>{
                try{
                    const a = num+1
                    a = 1 
                    res(a)//將正確的數據返出去 能夠用.then接
                }catch(e){
                    rej(e)//將錯誤的數據返出去 能夠用.then接
                }
            })
        })
    };
    
    
    foo_(100).then(
        success=>{//成功的回調 
            let result = success+10
            console.log(`success,成功的回調:${result}`)
            throw new Error( result)
        },
        error=>{//失敗的回調
            let err = error
            console.log(`error,失敗的回調:${err}`)
           throw new Error( '接住了 沒有註釋掉 a= 1 的錯誤')
        }
    ).then(
        success=>{
            let res = baz(success)
            console.log(`res=${res}`)
        },
        error=>{
            console.log(`這是一個錯誤:${error}`)
        }
    )
複製代碼

異步的傳染性 :

在.then中,return 或者 throw 拋出去的值都必須是promise 只有這樣才能夠繼續被下一個.then 中接到,至於進成功回調仍是失敗回調 那就看上一個函數的執行結果了,在同步函數中,默認return throw 拋出去的值都是promise,可是若是在.then中有異步操做就必須給想辦法拋出去一個promise 否則是接不到的 可是若是在.then當中沒有使用return 或者 throw 出一個promise 的話,那麼在下一個.then中是不會接到這個值的,而且若是是異步。我以爲這個能夠被稱爲異步的傳染性

剛纔咱們在.then中一直演示的是同步操做,若是.then中有異步操做,可不能夠繼續傳遞數據呢?固然是能夠的!!話很少說,直接上代碼。

function foo_(num){
        return new Promise((res,rej)=>{
            setTimeout(()=>{
                try{
                    const a = num+1
                    res(a)//將正確的數據返出去 能夠用.then接
                }catch(e){
                    rej(e)//將錯誤的數據返出去 能夠用.then接
                }
            },2000)
        })
    };


 foo_(100).then(
        success=>{//成功的回調 
            setTimeout(()=>{
                let result = success+10
                return result
            },2000)
        },
        error=>{//失敗的回調
            let err = error
            console.log(`error,失敗的回調:${err}`)
           throw new Error( '接住了 沒有註釋掉 a= 1 的錯誤')
        }
    ).then(
        success=>{
            setTimeout(()=>{
                return success+10
            },2000)
        },
        error=>{
            console.log(`這是一個錯誤:${error}`)
        }
    ).then((res)=>{
        console.log('6秒以後:',res)
        return res
    })
複製代碼

打印結果:

這究竟是爲何? 其實在前文我就說過,promise具備傳染性,因此若是想要繼續傳遞這個值,就必須拋出一個promise 那麼想要成功的在6秒後打印出這個結果,咱們應該怎麼作呢?

foo_(100).then(
        success=>{//成功的回調 
          return new Promise((res,rej)=>{
            setTimeout(()=>{
              let value = success+10
              res(value)
          },2000) 
          })
        }
    ).then(
        success=>{
          return new Promise((res,rej)=>{
            setTimeout(()=>{
              res(success)
          },2000)
          })
        }
    ).then((res)=>{
        console.log('6秒以後:',res)
        return res
    })
複製代碼

打印結果:

在以上的每個異步中,我都返回了一個promise 因此他纔會成功的打印出來。那麼咱們來一段異步同步混合使用的promise

function foo(){
	return Promise.resolve(100)
};
foo().then((v)=>{
	return new Promise((res=>{
			setTimeout(()=>{
				res(v+100) //異步操做,返回promise實例利用res()傳遞結果
			},3000)
		}))
}).then((v)=>{
	v=v+100
	return v //同步操做,默認返回promise傳遞結果
}).then((v)=>{
	return new Promise((res)=>{
	setTimeout(()=>{
		res(v+100) //異步操做,返回promise實例利用res()傳遞結果
		},3000)
	})
}).then((v)=>{
	return v+100  //同步操做,默認返回promise傳遞結果
}).then((v)=>{
	return new Promise((res)=>{
		setTimeout(()=>{
			res(v+100)  //同步操做,默認返回promise傳遞結果
		})
	})
}).then((v)=>{
console.log(v+100)
	return v+100 //同步操做,默認返回promise傳遞結果
}).then((v)=>{
              //由於沒有return 因此下面接到的v是undefind
}).then((v)=>{
	console.log(v)
	return v
})
複製代碼

打印結果我就不貼了,看官們本身複製粘貼一下跑一下就曉得了。

再來一個混合了error狀況的狀況:

foo().then((v)=>{
	return new Promise((res=>{
			setTimeout(()=>{
				res(v+100)
			},3000)
		}))
}).then((v)=>{
	v=v+100
	return v
}).then((v)=>{
	return new Promise((res)=>{
	setTimeout(()=>{
			
		res(v+100)
		},3000)
	})
}).then((v)=>{
	return v+100
}).then((v)=>{
	return new Promise((res)=>{
		setTimeout(()=>{
			res(v+100)
		})
	})
}).then((v)=>{
	return v+100
}).then((v)=>{
	 throw (v+100) //此處爲error回調
}).then((v)=>{
	console.log('this is resolve' ,v+100) //不會被打印
	return v+100
},(e)=>{
	console.log('this is error',e+100)
	return e+100
}).then((v)=>{
	console.log('this is v' ,v+100)
	return v+100
})
複製代碼


簡單的總結一下:

一個異步函數中,new Promise 並將這個promise返回(return):
function foo(){
    return new Promise()
    //return Promise.resolve().
}

new promise時 向該構造函數內傳一個函數,該函數接受兩個參數:
表示成功返回的 res 相似 return
表示失敗的失敗返回 rej 相似 throw
function foo(){
    return new Promise((res,rej)=>{
        
    })
}

在這個函數中進行異步操做,成功的值用res(result)拋出去,相似 return result
錯誤的值用rej(error)拋出去 相似 throw error
function foo(num){
        return new Promise((res,rej)=>{
            setTimeout(()=>{
                try{
                    const a = num+1
                    //a = 1 
                    res(a)
                }catch(e){
                    rej(e)
                }
            })
        })
    };
    
    
使用 .then 去接 foo 拋出去的值
.then 接受兩個參數,即成功的回調,失敗的回調
foo(100).then(
        success=>{//成功的回調
            console.log(`success,成功的回調:${success}`)
        },
        error=>{//失敗的回調
            console.log(`error,失敗的回調:${error}`)
        }
    )
複製代碼

關於promise,其實還有.catch,promise.all()等函數。 要再說下去就太多了,若是各位有興趣的話,我會在後面繼續介紹.catch promise.all 等一系列promise衍生出來的東西。 第一次寫東西,可能寫的不太好,請你們見諒~

相關文章
相關標籤/搜索