使用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 相关文章推荐
Jquery+JSon 无刷新分页实现代码
Apr 01 Javascript
javascript字符串拼接的效率问题
Dec 25 Javascript
为开发者准备的10款最好的jQuery日历插件
Feb 04 Javascript
JS实现仿FLASH效果的竖排导航代码
Sep 15 Javascript
bootstrap weebox 支持ajax的模态弹出框
Feb 23 Javascript
详解angularJs模块ui-router之状态嵌套和视图嵌套
Apr 28 Javascript
Angular利用trackBy提升性能的方法
Jan 26 Javascript
vue-cli实现多页面多路由的示例代码
Jan 30 Javascript
简单明了区分escape、encodeURI和encodeURIComponent
May 26 Javascript
微信小程序实现天气预报功能
Jul 18 Javascript
vue移动端屏幕适配详解
Apr 30 Javascript
在vue中使用axios实现post方式获取二进制流下载文件(实例代码)
Dec 16 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
一些花式咖啡的配方
2021/03/03 冲泡冲煮
php面向对象全攻略 (五) 封装性
2009/09/30 PHP
PHP闭包函数传参及使用外部变量的方法
2016/03/15 PHP
今天你说520了吗?不仅有php表白书还有java表白神器
2016/05/20 PHP
PHP实现简易用户登录系统
2020/07/10 PHP
jQuery(1.3.2) 7行代码搞定跟随屏幕滚动的层
2009/05/21 Javascript
基于jQuery的遍历同id元素 并响应事件的代码
2012/06/14 Javascript
js简单实现根据身份证号码识别性别年龄生日
2013/11/29 Javascript
JS实现从连接中获取youtube的key实例
2015/07/02 Javascript
jQuery实现响应鼠标事件的图片透明效果【附demo源码下载】
2016/06/16 Javascript
JS实现表单多文件上传样式美化支持选中文件后删除相关项
2016/09/30 Javascript
基于vue.js轮播组件vue-awesome-swiper实现轮播图
2017/03/17 Javascript
vue2.0实战之基础入门(1)
2017/03/27 Javascript
js获取一组日期中最近连续的天数
2017/05/25 Javascript
详谈AngularJs 控制器、数据绑定、作用域
2017/07/09 Javascript
实例讲解Vue.js中router传参
2018/04/22 Javascript
vue 解决循环引用组件报错的问题
2018/09/06 Javascript
详解Vue一个案例引发「内容分发slot」的最全总结
2018/12/02 Javascript
Layui表格监听行单双击事件讲解
2019/11/14 Javascript
Vue项目中使用flow做类型检测的方法
2020/03/18 Javascript
使用Python获取网段IP个数以及地址清单的方法
2018/11/01 Python
如何运行.ipynb文件的图文讲解
2019/06/27 Python
python3.x+pyqt5实现主窗口状态栏里(嵌入)显示进度条功能
2019/07/04 Python
Django 导出项目依赖库到 requirements.txt过程解析
2019/08/23 Python
python入门之井字棋小游戏
2020/03/05 Python
pycharm下配置pyqt5的教程(anaconda虚拟环境下+tensorflow)
2020/03/25 Python
python抢购软件/插件/脚本附完整源码
2021/03/04 Python
HTML5本地存储之Web Storage详解
2016/07/04 HTML / CSS
在线实验室测试:HealthLabs.com
2020/05/03 全球购物
写出SQL四条最基本的数据操作语句(DML)
2012/12/12 面试题
什么是SCM(软件配置管理)
2014/08/16 面试题
高考诚信考试承诺书
2015/04/29 职场文书
关于vue中如何监听数组变化
2021/04/28 Vue.js
OpenCV图像变换之傅里叶变换的一些应用
2021/07/26 Python
Nginx 常用配置
2022/05/15 Servers
Redis实现主从复制方式(Master&Slave)
2022/06/21 Redis