使用Promise链式调用解决多个异步回调的问题


Posted in Javascript onJanuary 15, 2017

介绍

所谓Promise,简单来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

缺少场景支撑,对于新手而言,很难理解Promise的意义。

在《你不知道的JavaScript中》有个场景介绍得很形象:

我走到快餐店的柜台,点了一个芝士汉堡。我交给收银员1.47美元。通过下订单并付款,我已经发出了一个对某个值(就是那个汉堡)的请求。我已经启 动了一次交易。

但是,通常我不能马上就得到这个汉堡。收银员会交给我某个东西来代替汉堡:一张带有 订单号的收据。订单号就是一个 IOU(I owe you, 我欠你的)承诺(promise),保证了最 终我会得到我的汉堡。

所以我得好好保留我的收据和订单号。我知道这代表了我未来的汉堡,所以不需要担心, 只是现在我还是很饿!

在等待的过程中,我可以做点其他的事情,比如给朋友发个短信:“嗨,要来和我一起吃 午饭吗?我正要吃芝士汉堡。”

我已经在想着未来的芝士汉堡了, 尽管现在我还没有拿到手。 我的大脑之所以可以这么 做,是因为它已经把订单号当作芝士汉堡的占位符了。从本质上讲,这个占位符使得这个 值不再依赖时间。这是一个未来值。

终于, 我听到服务员在喊“订单 113” , 然后愉快地拿着收据走到柜台, 把收据交给收银 员,换来了我的芝士汉堡。

换句话说, 一旦我需要的值准备好了, 我就用我的承诺值(value-promise)换取这个值 本身。

但是,还可能有另一种结果。他们叫到了我的订单号,但当我过去拿芝士汉堡的时候,收 银员满是歉意地告诉我:“不好意思,芝士汉堡卖完了。”除了作为顾客对这种情况感到愤 怒之外,我们还可以看到未来值的一个重要特性:它可能成功,也可能失败。

每次点芝士汉堡,我都知道最终要么得到一个芝士汉堡,要么得到一个汉堡包售罄的坏消息,那我就得找点别的当午饭了。

所以Promise的出现其实是作为异步编程的一种解决方案。比传统的解决方案-回调函数和事件-更加合理、强大。

Promise的基本用法

var p1 = new Promise((resolve, reject) => {
 setTimeout(resolve, 1000, 'done');
 })
p1.then(data=>{
 console.log(data); // done
})

Promise一个明显的好处便是可以用来解决回调地狱。特别是在处理多个回调相互依赖的情况。

使用Promise解决多个异步依赖调用

Promise提供了一个方法Promise.all([p1,p2,p3]) ,用于将多个Promise实例,包装成一个新的Promise实例。接收的参数是一个数组,p1、p2、p3都是Promise对象。

此时Promise.all的状态取决于它的参数。

分两种情况:

  • p1、p2、p3的状态都是resolve的时候,Promise.all的状态才会变成resolve;
  • 只要p1、p2、p3中有一个的状态为reject,那么Promise.all的状态就会变成reject;

所以我们可以用Promise.all()来解决多个异步依赖调用。

比如我们平常经常遇到的一种情况:

网站中需要先获取用户名,然后再根据用户名去获取用户信息。这里获取用户名getUserName()和获取用户信息getUser()都是调用接口的异步请求。在获取用户信息之前,需要先获得用户名。也就是说getUser依赖于getUserName的状态。所以我们可以将这两个请求通过Promise.all()封装成一个新的Promise对象。

function getUserPromise(promiseX, promiseY){
 return Promise.all([promiseX, promiseY])
 .then(values =>
 // 返回的values由 promiseX 与 promiseY返回的值所构成的数组。
  values
 )
}
function getUserName(){
 let data = 'superman';
 return new Promise((resolve, reject) => {
  setTimeout(resolve(data), 1000);
 })
}
function getUser(){
 let data = {
 id:1,
 username: 'superman',
 gender: 'male'
 }
 return new Promise((resolve, reject) => {
 setTimeout(resolve(data), 2000);
 })
}
getUserPromise(getUserName(), getUser())
.then(data => {
 // 这里的data就是包含了getUserName 和 getUser返回值所组成的数组
 console.log(data); // [ 'superman', { id: 1, username: 'superman', gender: 'male' } ]
 })

使用Promise的链式调用

function getUserName(){
 let data = 'superman';
 return new Promise((resolve, reject) => {
 setTimeout(resolve(data), 4000);
 })
}
function getUser(username){
 let data = {
 id:1,
 username: 'superman',
 gender: 'male'
 }
 return new Promise((resolve, reject) => {
 if(username){
  setTimeout(resolve(data), 2000);
 }
 else{
  reject('err');
 }
 })
}
getUserName().then(username => {
 return getUser();
})
.then(user => {
 console.log(user);
})
.catch(err => {
 console.log(err);
})

有了Promise的链式调用,再也不同担心回调地狱的问题了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Javascript 相关文章推荐
JavaScript随机排序(随即出牌)
Sep 17 Javascript
Jquery 的扩展方法总结
Oct 01 Javascript
js批量设置样式的三种方法不推荐使用with
Feb 25 Javascript
JavaScript的各种常见函数定义方法
Sep 16 Javascript
微信小程序 获取设备信息 API实例详解
Oct 02 Javascript
谈谈因Vue.js引发关于getter和setter的思考
Dec 02 Javascript
el表达式 写入bootstrap表格数据页面的实例代码
Jan 11 Javascript
使用jQuery的ajax方法向服务器发出get和post请求的方法
Jan 13 Javascript
微信小程序中使用wxss加载图片并实现动画效果
Aug 13 Javascript
js实现京东秒杀倒计时功能
Jan 21 Javascript
vue 添加和编辑用同一个表单,el-form表单提交后清空表单数据操作
Aug 03 Javascript
vue print.js打印支持Echarts图表操作
Nov 13 Javascript
js实现密码强度检验
Jan 15 #Javascript
JavaScript实现图像模糊化的方法实例
Jan 15 #Javascript
vue实现简单实时汇率计算功能
Jan 15 #Javascript
bootstrap——bootstrapTable实现隐藏列的示例
Jan 14 #Javascript
JS轮播图中缓动函数的封装
Nov 25 #Javascript
JavaScript字符串对象
Jan 14 #Javascript
jquery mobile移动端幻灯片滑动切换效果
Apr 15 #Javascript
You might like
WAR3重制版DOTA 5V5初体验
2020/04/09 DOTA
PHP下10件你也许并不了解的事情
2008/09/11 PHP
推荐一款MAC OS X 下php集成开发环境mamp
2014/11/08 PHP
总结PHP删除字符串最后一个字符的三种方法
2016/08/30 PHP
PHP简单实现模拟登陆功能示例
2017/09/15 PHP
一个基于jquery的图片切换效果
2010/07/06 Javascript
javascript 学习笔记(八)javascript对象
2011/04/12 Javascript
Js注册协议倒计时的小例子
2013/06/24 Javascript
JS复制内容到剪切板的实例代码(兼容IE与火狐)
2013/11/19 Javascript
基于javascript实现窗口抖动效果
2016/01/03 Javascript
jquery zTree异步加载简单实例讲解
2016/02/25 Javascript
详解js的延迟对象、跨域、模板引擎、弹出层、AJAX【附实例下载】
2016/12/19 Javascript
bootstrap下拉菜单使用方法解析
2017/01/13 Javascript
微信小程序 图片加载(本地,网路)实例详解
2017/03/10 Javascript
基于JavaScript实现瀑布流效果
2017/03/29 Javascript
详解vue2.0 transition 多个元素嵌套使用过渡
2017/06/19 Javascript
VUE 配置vue-devtools调试工具及安装方法
2018/09/30 Javascript
vue-music 使用better-scroll遇到轮播图不能自动轮播问题
2018/12/03 Javascript
Makefile/cmake/node-gyp中区分判断不同平台的方法
2018/12/18 Javascript
详解angular2 控制视图的封装模式
2018/12/27 Javascript
小程序实现自定义导航栏适配完美版
2019/04/02 Javascript
[04:00]黄浦江畔,再会英雄——完美世界DOTA2 TI9应援视频
2019/07/31 DOTA
python实现根据用户输入从电影网站获取影片信息的方法
2015/04/07 Python
解决Linux系统中python matplotlib画图的中文显示问题
2017/06/15 Python
Python2.7环境Flask框架安装简明教程【已测试】
2018/07/13 Python
解决每次打开pycharm直接进入项目的问题
2018/10/28 Python
用django-allauth实现第三方登录的示例代码
2019/06/24 Python
Python空间数据处理之GDAL读写遥感图像
2019/08/01 Python
python3 实现的对象与json相互转换操作示例
2019/08/17 Python
Python中的单下划线和双下划线使用场景详解
2019/09/09 Python
Python Numpy数组扩展repeat和tile使用实例解析
2019/12/09 Python
python实现指定ip端口扫描方式
2019/12/17 Python
python中线程和进程有何区别
2020/06/17 Python
H5 video poster属性设置视频封面的方法
2020/05/25 HTML / CSS
导游词之苏州阳澄湖
2019/11/15 职场文书
详解Redis的三种常用的缓存读写策略步骤
2022/05/06 Redis