谈一下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衍生出来的东西。 第一次写东西,可能写的不太好,请你们见谅~

相关文章
相关标签/搜索