12个例子夯实promise基础
工作中常常用到promise
,async + await
,遇到一些问题需要用到基础知识总会有一部分不记得,就重新温习权威指南和es6标准入门,花了几天肝下了这篇文章。喜欢的同学请动动发财手点个赞,文章有错误的地方还请指出。
例一
new Promise((resolve,reject) =>{
console.log('11')
resolve()
console.log('Promise')
})
console.log('start')
结果:
11
Promise
start
分析:
- Promise 新建后就会立即执行,从上往下执行,打印
11
- 然后碰到
resolve()
,Promise状态由pending
变为fulfilled
- 但调用
resolve
或reject
并不会终结Promise
的参数函数的执行,所以会往下执行,打印出Promise
- 继续往下,打印出
start
总结:调用resolve或reject并不会终结 Promise 的参数函数的执行
再看一个代码类似的例子但执行过程却不一样
例二
new Promise((resolve, reject) => {
return resolve(1);
console.log(2);
}).then((res) =>{
console.log('then',res)
})
结果:
then 1
分析:
- Promise 新建后就会立即执行,碰到
resolve(1)
,Promise状态由pending
变为fulfilled
- 但这里加上了
return
,console.log(2)不会执行 - 执行
.then()
,打印出 then 1
结果:Promise参数函数中加上return,后面的语句不会执行了,2
不会被打印。.then()
和.catch()
中也是一样。
猜猜下面的执行会是什么
new Promise((resolve, reject) => {
resolve(1);
}).then((res) =>{
return 2;
console.log('then1',res)
}).then((res) =>{
console.log('then2',res)
throw new Error('error')
}).catch((err) =>{
return 'catch返回值'
console.log('catch',err)
}).then((res) =>{
console.log('then3',res)
})
结果:
then2 2
then3 catch返回值
分析:
- new Promise立即执行,
resolve(1)
将1
作为返回值传给.then()
- 执行
.then()
,遇到return 2
,相当于return Promise.resolve(2)
,由于加了return,后面的 then1不会被打印 - 执行第二个
.then()
,打印then2 2
,然后抛出一个错误,相当于执行了return Promise.reject('error')
- 往下执行,
.catch()
捕获到了错误,并return 'catch返回值'
,相当于return Promise.resolve('catch返回值')
,同样下面的 cosnole.log('catch',err)不会执行 - 最后的
.then()
会接收到上面的返回值,并打印then3 catch
返回值
例三
Promise.resolve('start')
.then((res) =>{
console.log('then1',res)
return 11 //相当于 return Promise.resolve(11)
}).then((res) =>{
console.log('then2',res) //相当于 return Promise.resolve(undefined)
}).then((res) =>{
console.log('then3',res)
throw new Error('then3 error') //相当于 return Promise.reject(new Error('then3 error'))
}).catch((res) =>{
console.log('catch',res)
})
结果:
then1 start
then2 11
then3 undefined
catch Error: then3 error
分析:在Promise
中,返回任意一个非 promise
的值都会被包裹成promise
对象
例四
const promise = new Promise((resolve, reject) => {
console.log(1);
console.log(2);
});
promise.then(() => {
console.log(3);
});
console.log(4);
结果:
1 2 4
分析:
- new Promise立即执行,打印
1
和2
, - 遇到promise.then(),由于Promise中没有
resolve()
或者reject()
,所以promise的状态会一直是pending
,promise.then()方法不会执行,3
不会被打印, - 继续往下,打印
4
总结:promise里面没有resolve()或reject(),promise会一直是pending
状态,并且不会执行对应的.then()
或者.catch()
例五:.then()
的参数
new Promise((resolve,reject) =>{
resolve('start')
})
.then(1)
.then(Promise.resolve(2))
.then((res) =>{
console.log(res)
})
结果:
start
分析:
- Promise的
.then
或者.catch
的参数期望是函数,传入非函数则会发生值透传,所以.then(1)
相当于.then((res) => Promise.resolve(res))
, .then(Promise.resolve(2))
相当于
.then((res) => {
Promise.resolve(2)
return Promise.resolve(res)
})
- 最后的
.then()
打印start
总结:.then
或者 .catch
的参数期望是函数,传入非函数则会发生值透传
将上面例子中再加入一行.then(console.log)
,打印结果又有所不同了
new Promise((resolve,reject) =>{
resolve('start')
})
.then(1)
.then(Promise.resolve(2))
.then(console.log)
.then((res) =>{
console.log('res',res)
})
结果:
start
res undefined
分析:
start
是由第三个.then()
打印的,.then(console.log)
相当于
.then((res) =>{
console.log(res)
return Promise.resolve(undefined)
})
例六:Promise
的状态
const p = new Promise((resolve,reject) =>{
resolve('11')
})
p.then(() =>{
console.log('then1',p)
})
p.then(() =>{
console.log('then2',p)
})
结果:
then1 Promise {<fulfilled>: '11'}
then2 Promise {<fulfilled>: '11'}
分析:Promise
的状态一经改变就不能再改变
例七:.catch()
处理错误
new Promise((resolve,reject) =>{
resolve()
}).then((res) =>{
throw new Error('throw error')
},(err) =>{
console.log('err',err)
}).catch((err) =>{
console.log('catch:',err)
})
结果:
catch: Error: throw error
分析:对promise而言,处理错误可以给.then()
方法传递第二个函数,但问题在于.then()
中的第二个函数无法捕获第一个函数出现的错误。而用.catch()
则能捕获到上层抛出的错误
例八:.catch()
回调
new Promise((resolve,reject) =>{
reject()
}).catch((err) =>{
return 'catch1 正常返回了'
}).then((res) =>{
console.log('then1:',res)
}).catch((err) =>{
console.log('catch2:',err)
})
结果:
then1: catch1 正常返回了
分析:
- new Promise中
reject()
,promise
状态由pending
转为rejected
- 执行
.catch()
,并return 'catch1 正常返回了',这里相当于Promise.resolve('catch1 正常返回了')
- 执行
.then()
,打印then1
总结:.catch()
回调正常返回,则返回值会传递给与之关联的promise
而在.catch()
中抛出错误,则会被下一个.catch()
捕获,如果没有再定义.catch()
则错误会直接抛出
new Promise((resolve,reject) =>{
reject()
}).catch((err) =>{
throw new Error('ee')
}).then((res) =>{
console.log('then1:',res)
}).catch((err) =>{
console.log('catch2:',err)
})
结果:
catch2: Error: ee
例九
Promise.reject('start')
.catch((res) =>{
console.log('catch1',res)
return new Error('new error1')
}).then((res) =>{
console.log('then1',res)
return new Error('new error2')
}).then((res) =>{
console.log('then2',res)
}).catch((err) =>{
console.log('catch2',err)
})
结果:
catch1 start
then1 Error: new error1
then2 Error: new error2
分析:
- 从上往下执行
start
将会被.catch()
捕获,并打印catch1 start
- 继续执行,
.catch()
返回一个promise
包装的错误对象error1
,注意这里是错误对象,会被当前一个对象来处理,而不是一个错误;相当于Promise.resolve(new Error('error1'))
;所以.catch()
返回值会被紧接着的.then()
方法接收到,并打印then1 Error: new error1
- 继续执行,
.then()
又返回了一个promise
包装的错误对象error2
,同样这里不会当成错误被最后的.catch()
捕获,而是会被下一个.then()
接收到,此时打印了then2 Error: new error2
总结:.then
或者 .catch
中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获
例十
const p = new Promise((resolve,reject) =>{
resolve(11)
}).then(() =>{
return p
})
结果:
Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
分析:.then()
或 .catch()
返回的值如果是 promise 本身,会造成死循环;就像无线回调,不停的往下注册、调用.then()
例十一
Promise.resolve('start')
.finally((res1) =>{
console.log('finally',res1)
return 'finally'
}).then((res2) =>{
console.log('then',res2)
}).finally(() =>{
throw new Error('error')
}).catch((res3) =>{
console.log('catch',res3)
})
结果:
finally undefined
then start
catch Error: error
分析:
finally
的回调函数接收不到promise的结果,所以res1
返回的是undefined
finally
的返回值没有抛出错误的情况下默认是上一个promise
的返回值,所以.then()
方法接收的实际上是Promise.resolve('start')
的返回值,打印的是start
- 然后
.then()
方法没有return
任何值,此时会等同于Promise.resolve('undefined')
- 往下执行又碰到了
finally
,此时.finally()
方法抛出了一个错误,会被下一个最近的.catch()
方法捕获,所以res3
打印了Error:error
例十二:串行promise
传入一个url
组成的数组,按顺序执行数组中的url
并返回结果,下面例子的fetchUrl
函数为了测试可以设置setTimeout
,而在实际开发中类似长这样:
function fetchUrl(url){
return fetch(url)
.then((response) =>{
return response.text()
}).then((val) =>{
arr.push(val)
})
}
promise实现
var urls = [1,2,3,4]
function fetchAll(urls){
let arr = []
let p = Promise.resolve(undefined)
function fetchUrl(url){
return new Promise((resolve,reject) =>{
setTimeout(() =>{
console.log(url)
resolve(url*2)
},1000)
})
}
for (let url of urls) {
p = p.then((function(url){
return () => fetchUrl(url)
})(url)).then((res) =>{
arr.push(res)
})
}
return p.then(() => arr)
}
fetchAll(urls).then((res) =>{
console.log(res)
})
分析:
fetchAll
里面定义fetchUrl
函数,该函数会去调用url
对应的接口,返回值会用promise
包装,并将返回值保存在arr
中- 这里需要注意的是,如果在循环中写
p = p.then(() => fetchUrl(url))
,那么这个url
将会是数组中的最后一个url
- 因为要
p.then()
中的参数回调是一个异步任务,在循环时参数回调会被放入微任务队列
,不会立即执行,导致不能绑定正确的url
- 所以在
for of
循环中p.then()
的参数回调需要用立即执行表达式绑定每个url
(或者用forEach
循环,forEach
循环的内部会给每个元素执行一遍回调函数)
结果:
1
2
3
4
[2, 4, 6, 8]
async await 实现
var urls = [1,2,3,4]
function fetchUrl(url){
return new Promise((resolve,reject) =>{
setTimeout(() =>{
console.log(url)
resolve(url*2)
},1000)
})
}
async function awaitFetch(urls) {
let arr = []
for (let url of urls){
let p = await fetchUrl(url)
arr.push(p)
}
console.log(arr)
return arr
}
awaitFetch(urls)
分析:await
会等后面的fetch(url)
返回的promise
resolve()
之后,才会执行后面的代码,在此之前,await
阻塞之后的代码执行,所以这个for of
循环相当于同步循环了
结果:
1
2
3
4
[2, 4, 6, 8]
生成器实现
var urls = [1,2,3,4]
function fetchUrl(url){
return new Promise((resolve,reject) =>{
setTimeout(() =>{
console.log(url)
resolve(url*2)
},1000)
})
}
async function* fetchGen(urls){
for (let url of urls){
let p = await fetchUrl(url)
yield p
}
}
async function fetchAllByGen(urls) {
let arr = []
for await(let p of fetchGen(urls)){
arr.push(p)
}
console.log(arr)
return arr;
}
fetchAllByGen(urls)
分析:
for of
循环专门用于可迭代对象,而生成器就是一个迭代器对象。fetchGen
是一个异步生成器函数- 在
for await
循环中,每次循环会等待fetchGen(url)
返回结果,再往下执行arr.push(p)
的操作 - 为了测试,将
fetchUrl
函数中的fetch api
改成了setTimeout
结果:
1
2
3
4
[2, 4, 6, 8]
12个例子夯实promise基础的更多相关文章
- 夯实Java基础系列1:Java面向对象三大特性(基础篇)
本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 [https://github.com/h2pl/Java-Tutorial](https: ...
- 夯实Java基础系列9:深入理解Class类和Object类
目录 Java中Class类及用法 Class类原理 如何获得一个Class类对象 使用Class类的对象来生成目标类的实例 Object类 类构造器public Object(); register ...
- 夯实Java基础系列10:深入理解Java中的异常体系
目录 为什么要使用异常 异常基本定义 异常体系 初识异常 异常和错误 异常的处理方式 "不负责任"的throws 纠结的finally throw : JRE也使用的关键字 异常调 ...
- 夯实Java基础系列14:深入理解Java枚举类
目录 初探枚举类 枚举类-语法 枚举类的具体使用 使用枚举类的注意事项 枚举类的实现原理 枚举类实战 实战一无参 实战二有一参 实战三有两参 枚举类总结 枚举 API 总结 参考文章 微信公众号 Ja ...
- 【RL-TCPnet网络教程】第12章 TCP传输控制协议基础知识
第12章 TCP传输控制协议基础知识 本章节为大家讲解TCP(Transmission Control Protocol,传输控制协议),通过本章节的学习,需要大家对TCP有个基本的认识,方 ...
- 夯实Java基础(十一)——内部类
1.内部类的概念 内部类顾名思义:将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类.对于很多Java初学者来说,内部类学起来真的是一头雾水,根本理解不清楚是个什么东西,包括我自己(我太菜 ...
- 夯实Java基础系列3:一文搞懂String常见面试题,从基础到实战,更有原理分析和源码解析!
目录 目录 string基础 Java String 类 创建字符串 StringDemo.java 文件代码: String基本用法 创建String对象的常用方法 String中常用的方法,用法如 ...
- 夯实Java基础系列4:一文了解final关键字的特性、使用方法,以及实现原理
目录 final使用 final变量 final修饰基本数据类型变量和引用 final类 final关键字的知识点 final关键字的最佳实践 final的用法 关于空白final final内存分配 ...
- 夯实Java基础系列5:Java文件和Java包结构
目录 Java中的包概念 包的作用 package 的目录结构 设置 CLASSPATH 系统变量 常用jar包 java软件包的类型 dt.jar rt.jar *.java文件的奥秘 *.Java ...
- 夯实Java基础系列6:一文搞懂抽象类和接口,从基础到面试题,揭秘其本质区别!
目录 抽象类介绍 为什么要用抽象类 一个抽象类小故事 一个抽象类小游戏 接口介绍 接口与类相似点: 接口与类的区别: 接口特性 抽象类和接口的区别 接口的使用: 接口最佳实践:设计模式中的工厂模式 接 ...
随机推荐
- 「Codeforces 1131D」Gourmet Choice
Description 美食家 Apple 先生是一家美食杂志的主编.他会用一个正整数来评价每一道菜. 美食家在第一天品尝第 $n$ 道菜,第二天品尝了 $m$ 道菜.他制作了一张 $n\times ...
- Codeforces 1326A Bad Ugly Numbers (思维)
Codeforces 1326A Bad Ugly Numbers 看完题目,第一直觉,质数肯定满足题意,再看数据范畴,\(1≤n≤10^5\), 质数线性筛仅能做到 n=7 的情况,即处理到1000 ...
- WebGPU光追引擎基础课:课程介绍
大家好~我开设了"WebGPU光追引擎基础课"的线上课程,从0开始,在课上带领大家现场写代码,使用WebGPU开发基础的光线追踪引擎 课程重点在于基于GPU并行计算,实现BVH构建 ...
- 移动端h5中rem适配
1 (function (win, lib) { 2 var doc = win.document; 3 var docEl = doc.documentElement; 4 var metaEl = ...
- 从零开始制作PyTorch的Singularity容器镜像
技术背景 在前面的博客中,我们大篇幅的使用到了Docker和Singularity这两种常见的容器化编程环境解决方案,使得我们的各个编程环境能够更好的隔离.如果要展开讲解容器化编程环境的重要性的话,我 ...
- C# WPF:这次把文件拖出去!
首发公众号:Dotnet9 作者:沙漠之尽头的狼 编辑于:成都,2020-12-01 回顾上篇文章:C# WPF:把文件给我拖进来!!! 本文完成对应的下文:<C# WPF:这次把文件拖出去!& ...
- C++ 语法结构--堆
1.堆介绍 「堆 heap」是一种满足特定条件的完全二叉树,主要可分为图 8-1 所示的两种类型. 「大顶堆 max heap」:任意节点的值 其子节点的值. 「小顶堆 min heap」:任意节点的 ...
- [转帖]Docker与k8s的恩怨情仇(四):云原生时代的闭源落幕
https://zhuanlan.zhihu.com/p/388840887 在本系列前几篇文章中,我们介绍了从Cloud Foundry到Docker等PaaS平台的发展迭代过程.今天我们继续来为大 ...
- iftop的学习与使用
iftop的学习与使用 背景 前段时间一直进行netperf 等网络性能验证工具的学习与使用. 监控很多时候采用了 node-exporter + prometheus + grafana来进行观察 ...
- [转帖]通过 TiUP 部署 TiDB 集群的拓扑文件配置
https://docs.pingcap.com/zh/tidb/stable/tiup-cluster-topology-reference 通过 TiUP 部署或扩容 TiDB 集群时,需要提供一 ...