深入理解javascript系列(十九):从Promise开始到async/await
什么是同步与异步的定义,在这里我就不做记录,直接用代码来表示它们之间的区别。
首先使用Promise模拟一个发起请求的函数,该函数执行后,会在1s之后返回数值30。
function fn() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(30);
}, 1000);
})
在该函数的基础上,我们也可以使用async/await语法来模拟同步效果。
var foo = async function() {
var t = await fn();
console.log(t);
console.log('next');
}
foo();
输出结果为:
Promise {<pending>} //1s 之后依次输出
test:11 30
test:12 next
而异步效果则会有不同的输出结果:
var foo = function() {
fn().then(function(res) {
console.log(res);
});
console.log('next');
}
输出结果:
next
// 1s后
30
好了,接下来我们正式开始记录Promise
Promise
1. Ajax
Ajax是网页与服务端进行数据交互的一种技术。我们可以通过服务端提供的接口,用Ajax向服务端请求我们需要的数据。过程如下:
//简单的Ajax原生实现
//服务端接口
var url = 'api/xxxx';
var result;
var XHR = new XMLHttpRequest();
XHR.open('GET', url, true);
XHR.send();
XHR.onreadystatechange = function() {
if(XHR.readyState == 4 && XHR.status == 200) {
result = XHR.response;
}
}
这样看上去并没有什么问题。但是如果这个时候,还需要做另一个Ajax请求,那么这个新的Ajax请求中的一个参数,则必须从上一个Ajax请求中获取,这个时候我们就不得不就得在result得到后在进行一次请求。
当第三个Ajax(甚至更多)仍然依赖上一个请求的时候,此时的代码就变成了一场灾难。我们需要不停地嵌套回调函数,以确保下一个接口所需要的参数的正确性,这样的灾难,我们称为回调地狱。
所以随着发展,就出现了Promise,他能解决这个问题。
我们想要确保某代码在某某之后执行时,可以利用函数调用栈,将想要执行的代码放入回调函数中(这是利用同步阻塞)。
function a(callback) {
console.log('先结婚')
callback();
}
function b() {
console.log('再生孩子')
}
a(b);
插个题外话:“浏览器最早内置的setTimeout与setInterval就是基于回调的思想实现的”。
但是这里也有一个问题,我们想要在a中执行的代码必须现在callback之前才能输出我们想输出的。那该怎么办?
其实问题很好解决,除了利用函数调用栈的执行顺序外,还可以利用队列机制来确保我们想要的代码压后执行。
function a(callback) {
//将想要执行的代码放入队列中后,根据事件循环机制,
//就不用把它放到最后面了。
callback && setTimeout(callback, 0);
console.log('先结婚')
}
function b() {
console.log('再生孩子')
}
a(b);
与setTimeout类似,Promise也可以认为是一种任务分发器,它将任务分配到Promise队列中,通常的流程是首先发起一个请求,然后等待(等待时间没法确定)并处理请求结果。
var tag = true;
var p = new Promise(function(resolve, reject) {
if(tag) {
resolve('tag is true')
} else {
reject('tag is false')
}
})
p.then(function(result) {
console.log(result);
})
.catch(function(err) {
console.log(err);
})
下面简单介绍一下Promise的相关基础知识:
- new Promise表示创建一个Promise实例对象。
- Promise函数中的第一参数为一个回调函数,也可以称之为executor。通常情况下,在这个函数中,会执行发起请求操作,并修改结果的状态值。
- 请求结果有三种状态,分别是pending(等待中,表示还没有得到结果)、resolved(得到了我们想要的结果,可以继续执行),以及rejected(得到了错误的,或者不是我们期望的结果,拒绝继续执行)。请求结果的默认状态为pending。在executor函数中,可以分别使用resolve与rejected将状态修改为对应的resolved与rejected。resolve、reject是executor函数的两个参数,它们能够将请求结果的具体数据传递出去。
- Promise实例拥有的then方法,可以用来处理当请求结果的状态变成resolved时的逻辑。then的第一个参数为一个回调函数,该函数的参数是resolve传递出来的数据。在上面的例子中,result = tag is true。
- Promise实例拥有的catch方法,可用来处理当前请求结果的状态变成rejectd时的逻辑。catch的第一个参数为一个回调函数,该函数的参数是一个reject传递出来的数据。在上面的例子中,err = tag is false。
//demo01.js
function fn(num) {
//创建一个Promise实例
return new Promise(function(resolve, reject) {
if(typeof num == 'number') {
//修改结果状态值为resolved
resolve();
} else {
// 修改结果状态值为rejected
reject();
}
}).then(function() {
console.log('参数是一个number值');
}).catch(function() {
console.log('参数不是一个number值');
})
}
//修改参数的类型,观察输出的结果
fn('12');
//注意观察该语句的执行顺序
console.log('next code');
then方法可以接收两个参数,第一个参数用来处理resolved状态的逻辑,第二个参数用来处理rejected状态的逻辑。
then方法因为返回的仍是一个Promise实例对象,因此then方法可以嵌套使用。在这个过程中,通过在内部函数末尾return的方式,能够将数据持续往后传递。
下面我们来对Ajax进行一个简单的封装。
var url = 'api/xxxx';
//封装一个get请求的方法
function getJSON(url) {
return new Promise(function(resolve, reject) {
//利用Ajax发送一个请求
var XHR = new XMLHttpRequest();
XHR.open('GET', url, true);
XHR.send();
//等待结果
XHR.onreadystatechange = function() {
if(XHR.readyState == 4) {
if(XHR.status == 200) {
try {
var res = JSON.parse(XHR.responseText);
// 得到正确的结果修改状态并将数据传递出去
resolve(response);
} catch(e) {
reject(e)
}
} else {
// 得到错误的结果并抛出异常
reject(new Error(XHR.statusText));
}
}
}
})
}
//封装好以后,使用就很简单了
getJSON(url).then(function(res){
console.log(res)
})
2. Promise.all
当有一个Ajax请求,它的参数需要另外两个甚至更多个请求都有返回结果之后才能确定时,就需要用到Promise.all来帮助我们应对这个场景。
Promise.all接收一个Promise对象组成的数组作为参数,当这个数组中所有的Promise对象状态都变成resolved或者rejected时,它才会去调用then方法。
var url1 = 'xxx';
var url2 = 'xxxxx';
function renderAll() {
return Promise.all([getJSON(url1), getJSON(url2)]);
}
renderAll().then(function(value) {
console.log(value);
})
3. Promise.race
与Promise.all相似的是,Promise.race也是一个Promise对象组成的数组作为参数,不同的是,只要当数组中的其中一个Promise状态变成了resolved或者rejected时,就可以调用then方法。
async/await
异步问题不仅可以用Promise,还可以用async/await,都说这是终极解决方案。
async/await是ES7中新增的语法,虽然现在有些浏览器已经支持了该语法,但在实际使用中,仍然需要在构建工具中配置对该语法的支持才能放心使用。
在函数声明的前面,加上关键字async,这就是async的具体使用。
async function fn() {
return 30;
}
//或者
const fn = async ()=> {
return 30;
}
console.log(fn());
//打印结果
Promise {<resolved>: 30}__proto__:Promise[[PromiseStatus]]:"resolved"[[PromiseValue]]:30
可以发现打印结果是一个Promise对象,因此可以猜到async其实是Promise的一个语法糖,目的是为了让写法更加简单,因此也可以使用Promise的相关语法来处理后续的逻辑。
fn().then(res=>{
console.log(res);
})
await的含义是等待,意思就代码需要等待await后面的函数运行完并且有了返回结果之后,才继续执行下面的代码。这正是同步的效果。
但是需要注意的是,await关键字只能在async函数中使用,并且await后面的函数运行后必须返回一个Promise对象才能实现同步的效果。
当使用一个变量去接收await的返回值时,该返回值为Promise中resolve传递出来的值,也就是PromiseValue。
为了切实感受下async/await的用法。我们结合实际开发中最常遇到的异步请求接口的场景。
//先定义接口请求的方法,由于jQuery封装的几个请求方法都是返回Promise实例。
//因此可以直接使用async/await函数实现同步
const getUserInfo = () => $.get('api/asdsd');
const clickHandler = async ()=>{
try{
const res = await getUserInfo();
console.log(res);
// do something
} catch(e){
//处理错误逻辑
}
为了保证逻辑的完整性,在实践中try/catch必不可少。
原文发布时间为:2018年06月21日
原文作者:Panthon
本文来源: 掘金 如需转载请联系原作者

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
业余草,Java新人入职——配置环境及安装开发工具(总结)
很多新人对于进入新公司,相关工具的安装和环境变量的设定很苦恼。又苦于没有完整的配置开发环境的资料,我这里写一篇操作步骤的案例, 至少让你能把开发工具安装起来,并实用起来,就不会那么无助了。毕竟连开发环境都弄不好的人,leader对你肯定也不抱希望。废话少说,具体操作如下: 一、首先肯定是安装jdk 1、下载jdk版本 这里我的电脑是64位的,用的是Java8 , jdk版本是jdk1.8.0_92。 下载地址:链接:http://pan.baidu.com/s/1pLht3eF 密码:vgwu。 这里我也提供一个Java7 的版本:链接:http://pan.baidu.com/s/1hsOlk80 密码:i6k4 2、安装,并设定jdk、jre安装路径 安装路径你可以用默认的,但是一定要记住安装的地址,我一般指定额外的安装路径,比如E盘。如下图: 二、配置环境变量 安装好jdk和jre后,就需要配置环境变量,这个可以参照:点击打开链接 三、安装开发工具。 1、这里我用的是eclipse,具体开发工具用啥,大家看着处理:链接:https://pan.baidu.com/s/1MeNwU...
-
下一篇
Java匿名内部类与回调函数
之所以将匿名内部类和回调函数两个知识点一起写,是因为最近学习zookeeper的时候正好遇到这么一个例子。详细内容请参考:https://www.w3cschool.cn/zookeeper/zookeeper_api.html 以下是与ZooKeeper集合连接的完整代码。 public class ZooKeeperConnection { // declare zookeeper instance to access ZooKeeper ensemble private ZooKeeper zoo; final CountDownLatch connectedSignal = new CountDownLatch(1); // Method to connect zookeeper ensemble. public ZooKeeper connect(String host) throws IOException,InterruptedException { zoo = new ZooKeeper(host,5000,new Watcher() { public void pro...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Red5直播服务器,属于Java语言的直播服务器
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS8编译安装MySQL8.0.19
- MySQL数据库在高并发下的优化方案
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池