Promise基础知识
Promise
1.Promise的前置小知识
进程(厂房)
- 程序的运行环境
线程(工人)
线程是实际进行运算的东西
同步
- 通常情况代码都是自上向下一行一行执行的
- 前边的代码不执行后边的代码也不会执行
- 同步的代码执行会出现阻塞的情况
- 一行代码执行慢会影响到整个程序的执行
解决同步的问题:
java python
- 通过多线程来解决,但是一般消耗资源比较多
node.js
通过异步方式来解决
我们可以这么理解:客人就好比我们请求的数据,服务员就好比客户端,厨师就好比服务器,我们现在客人点菜,服务员接收到菜的名称信息,给厨师说,厨师开始做,厨师在做的时候,客人一直等,不能干其他的事情,这就是同步,只能干一件事,我们现在利用异步的方式,可以让客人在课桌上等着菜来,也不影响服务员接收下一个客人的点菜,这样就可以很好的处理同步所带来的堵塞问题
异步
- 一段代码的执行不会影响到其他的程序
- 异步的问题:
异步的代码无法通过
return
来设置返回值特点:
不会阻塞其他代码的执行
需要通过回调函数来返回结果
function sum(a, b, cb) {
setTimeout(() => {
cb(a + b) //调用箭头函数,把结果作为回调函数的参数
}, 1000)
} sum(123, 456, (result)=>{
console.log(result)
})
基于回调函数的异步带来的问题
代码的可读性差
可调试性差(造成回调地狱)
sum(123, 456, (result)=>{
sum(result, 7, (result)=>{
sum(result, 8, result => {
sum(result, 9, result => {
sum(result, 10, result => {
console.log(result)
})
})
})
})
})
解决问题:
- 需要一个东西,可以代替回调函数来给我们返回结果
Promise
横空出世- Promise是一个可以用来存储数据的对象
- Promise存储数据的方式比较特殊,这种特殊的方式使得Promise可以用来存储异步调用的数据
- Promise是一个可以用来存储数据的对象
2.Promise介绍
异步调用必须要通过回调函数来返回数据,当我们进行一些复杂的调用时,会出现回调地狱
问题:
异步必须通过回调函数来返回结果,回调函数增加就不容易处理
- Promise
- Promise可以帮助我们解决异步中的回调函数的问题
- Promise就是一个用来存储数据的容器
- 它拥有着一套特殊的存储数据的方式
- 这个方式使得它里面可以存储异步调用的结果
创建Promise
创建Promise时,构造函数中需要一个函数作为参数
Promise构造函数的回调函数,它会在创建Promise时调用,调用时会有两个参数传递进去
const promise = new Promise((resolve, reject)=>{
// resolve 和 reject 是两个函数,通过这两个函数可以向Promise中存储数据
// resolve 在执行正常的时候存储数据, reject 是在执行错误的时候存储数据
resolve('我是正常执行的时候调用的')
reject('我是错误执行的时候调用的')
//通过函数来访问Promise中添加数据,好处就是可以用来添加异步调用的数据
setTimeout(()=>{
resolve('异步中调用数据')
},2000)
throw new Error('出错了,调用的是reject')
})
从Promise中读取数据
可以通过Promise的实例方法
then
来读取Promise中存储的数据then需要两个回调作为参数,回调函数来获取Promise中的数据
通过resolve存储的数据,会调用第一函数返回,可以在第一个函数中编写处理数据的代码
通过reject存储数据或者出现异常时,会调用第二个函数返回,可以在第二个函数中编写处理异常的代码
promise.then((result)=>{
console.log('1',result)
},(reson)=>{
console.log('2',reason)
})
Promise中维护了两个隐藏属性:
- PromiseResult
- 用来存储数据
- PromiseState
- 记录Promise的状态(三种状态)
pending
(进行中)fulfilled
(完成)通过resolve存储数据时rejected
(拒绝,出错了)出错了或通过reject存储数据时
- state只能修改一次,修改以后永远不会在变
- 记录Promise的状态(三种状态)
- 流程:
- 当Promise创建时,PromiseState初始值为pending
- 当通过resolve存储数据时 PromiseState 变为fulfilled(完成)
- PromiseResult变为存储的数据
- 当通过reject存储数据或出错时 Promise 变为rejected(拒绝)
- PromiseResult变为存储的数据 或 异常对象
- 当通过resolve存储数据时 PromiseState 变为fulfilled(完成)
- 当我们通过then读取数据时,相当于为Promise设置了回调函数
- 如果PromiseState变为fulfilled,则调用then的第一个回调函数来返回数据
- 如果PromiseState变为rejected。则调用then的第二个回调函数来返回数据
- 当Promise创建时,PromiseState初始值为pending
const promise2 = new Promise((resolve, reject) => {
resolve("哈哈")
}) // console.log(promise2)
promise2.then(result => {
console.log(result)
}, reason => {
console.log("出错了")
})
- PromiseResult
catch()
用法和then类似,但是只需要一个回调函数作为参数catch() 中的回调只会在Promise被拒绝时才会调用
catch() 相当于 then(null, reason=>{})
catch() 就是一个专门处理Promise异常的方法
promise2.catch(reason => {
console.log(222222)
})
finally()
无论是正常存储数据还是出现异常了,finally总会执行
但是finally的回调函数中不会接收到数据
finally()通常用来编写一些无论成功与否都要执行的代码
promise2.finally(()=>{
console.log("没有什么能够阻挡我执行的!")
})
3.Promise详解
3.1Promise用法
Promise就是一个用来存储数据对象
,但是由于Promise存取的方式的特殊,所以可以直接将异步调用的结果存储到Promise中
function sum(a, b) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(a + b)
}, 1000)
})
}
//回调地狱式写法:
sum(123, 456).then(result => {
sum(result, 7).then(result =>{
sum(result, 8).then(result => {
console.log(result)
})
})
})
//promise写法:
sum(123, 456)
.then(result => result + 7)
.then(result => result + 8)
.then(result => console.log(result))
下来我们就解释一下为什么是这种链式调用
的形式:
promise中的then方法会返回一个新的Promise
,而我们接收并且输出的是Promise的PromiseResult的值,就像这样:then (return new Promise())
,
Promise中会存储回调函数的返回值,当前的参数是上一个链式调用的返回值
promise
.then(result => {
console.log("回调函数", result)
return "会作为下个then的参数"
})
.then(result => {
console.log("第二个then", result) //第二个then,会作为下个then的参数
return "taotao真快乐"
})
.then(result => {
console.log(result) //taotao真快乐
})
promise中的
- then (return new Promise())
- catch
- 这三个方法都会返回一个新的Promise, Promise中会存储回调函数的返回值
- finally
- finally的返回值,不会存储到新的Promise中
- 对Promise进行链式调用时,后面的方法(then和catch)读取的上一步的执行结果
- 如果上一步执行结果不是当前想要的结果,则跳过当前的方法,执行下一个方法
- 一般都把catch写到最后,只写一个,最后统一处理异常
- 当Promise出现异常时,而整个调用链中没有catch,则异常会向外抛出
3.2Promise静态方法
Promise.resolve()
创建一个立即完成的PromisePromise.resolve('成功调用时的数据').then(
(result)=>{
console.log(1111)
})
//相当于
new Promise((resolve, reject)=>{
resolve('成功调用时的数据')
}).then(result=>{
console.log(1111)
})
Promise.reject()
创建一个立即拒绝的PromisePromise.reject('错误')
Promise.all([...])
同时返回多个Promise的执行结果其中有一个报错,就返回错误
function sum(a, b){
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve(a + b)
},1000)
})
}
//传递一个可迭代对象(类数组)
Promise.all(
sum(111, 222),
Promise.reject("哈哈"),
sum(222, 333),
sum(333, 444)
).then((result)=>{
console.log(result) //[ 579, 11, 77 ]
}).catch ((reason)=>{
console.log(reason) //'哈哈'
})
Promise.allSettled([...])
同时返回多个Promise的执行结果(无论成功或失败){status: 'fulfilled', value: 579}
{status: 'rejected', reason: '哈哈'}
Promise.allSettled([
sum(123, 456),
sum(5, 6),
Promise.reject("哈哈"),
sum(33, 44)
]).then(r => {
console.log(r)
}) //返回的结果如下:
[
{ status: 'fulfilled', value: 579 },
{ status: 'fulfilled', value: 11 },
{ status: 'rejected', reason: '哈哈' },
{ status: 'fulfilled', value: 77 }
]
Promise.race([...])
返回执行最快的Promise(不考虑对错)Promise.race([
Promise.reject(1111),
sum(123, 456),
sum(5, 6),
sum(33, 44)
]).then(r => {
console.log(r)
}).catch(r=>{
console.log(r)
})
//执行结果如下
1111
Promise.reject(1111),不用等定时器执行结束,直接就调用
Promise.any([...])
返回执行最快的完成的PromisePromise.any([
Promise.reject(1111),
Promise.reject(2222),
Promise.reject(3333),
]).then(r => {
console.log(r)
}).catch(r => {
console.log("错误", r)
})
4.宏任务和微任务
JS是单线程的,它的运行时基于事件循环机制(event loop)
- 调用栈
- 栈
- 栈是一种数据结构,后进先出
- 调用栈中,放的是要执行的代码
- 栈
- 任务队列
- 队列
- 队列是一种数据结构,先进先出
- 任务队列的是将要执行的代码
- 当调用栈中的代码执行完毕后,队列中的代码才会按照顺序依次进入到栈中执行
- 在JS中任务队列有两种
- 宏任务队列 (大部分代码都去宏任务队列中去排队)
- 微任务队列 (Promise的回调函数(then、catch、finally))
- 队列
- 整个流程
- ① 执行调用栈中的代码
- ② 执行微任务队列中的所有任务
- ③ 执行宏任务队列中的所有任务
我们开始小试一下
Promise的执行原理
- Promise在执行,then就相当于给Promise了回调函数
当Promise的状态从pending 变为 fulfilled时,
then的回调函数会被放入到任务队列中
queueMicrotask() 用来向微任务队列中添加一个任务
console.log(1);
setTimeout(() => console.log(2));
Promise.resolve().then(() => console.log(3));
Promise.resolve().then(() => setTimeout(() => console.log(4)));
Promise.resolve().then(() => console.log(5));
setTimeout(() => console.log(6));
console.log(7);
// 1->7->3->5->2->6->4
5.async和await
正常我们创建一个异步的函数是
function fn(){
return Promise.resolve(10)
}
fn().then(r=>{
console.log(r) //10
})
通过async可以快速的创建异步函数,是Promise的一个语法糖
通过async创建的异步函数,异步函数的返回值会自动封装到一个Promise中返回
在async声明异步函数中可以使用await关键字来调用异步函数
async function fn2(){
return 10
}
Promise解决了异步调用中回调函数问题
虽然通过链式调用解决了回调地狱,但是链式调用太多以后还是不好看
我多想以同步的方式去调用异步的代码
- 当我们通过await去调用异步函数时,它会暂停代码的运行
- 直到异步代码执行有结果时,才会将结果返回
- 注意 await只能用于 async声明的异步函数中,或es模块的顶级作用域中
- await阻塞的知识异步函数内部的代码,不会影响外部代码
- 通过await调用异步代码时,需要通过try-catch来处理异常
function sum(a, b) {
return new Promise(resolve => {
setTimeout(() => {
resolve(a + b)
}, 2000);
})
}
//利用await调用
async function fn3(){
try {
let result = await sum(123, 456)
result = await sum(result, 8)
result = await sum(result, 9)
console.log(result)
} catch (e) {
console.log("出错了~~")
}
}
执行顺序的问题:
async function fn4() {
console.log(1)
/*
当我们使用await调用函数后,当前函数后边的所有代码
会在当前函数执行完毕后,被放入到微任务队里中
*/
await console.log(2)
// await后边的所有代码,都会放入到微任务队列中执行
console.log(3)
}
Promise基础知识的更多相关文章
- promise 基础知识
promise 基础知识 proise:1.Promise是异步编程的一种解决方案,它有三种状态,分别是pending-进行中.resolved-已完成.rejected-已失败2.创建实例//met ...
- 基础知识:Promise(整理)
基础知识:Promise(整理) (来自牛客网)下面关于promise的说法中,错误的是(D) A. resolve和reject都是直接生成一个进入相应状态的promise对象,其参数就是进入相应状 ...
- React Native 入门基础知识总结
中秋在家闲得无事,想着做点啥,后来想想,为啥不学学 react native.在学习 React Native 时, 需要对前端(HTML,CSS,JavaScript)知识有所了解.对于JS,可以看 ...
- C#复习笔记(5)--C#5:简化的异步编程(异步编程的基础知识)
异步编程的基础知识 C#5推出的async和await关键字使异步编程从表面上来说变得简单了许多,我们只需要了解不多的知识就可以编写出有效的异步代码. 在介绍async和await之前,先介绍一些基础 ...
- vue基础知识之vue-resource/axios
Vue基础知识之vue-resource和axios(三) vue-resource Vue.js是数据驱动的,这使得我们并不需要直接操作DOM,如果我们不需要使用jQuery的DOM选择器,就没 ...
- Vue基础知识之vue-resource和axios
Vue基础知识之vue-resource和axios 原文链接:http://www.cnblogs.com/Juphy/p/7073027.html vue-resource Vue.js是数据驱 ...
- Vue2基础知识学习
Vue2基础知识学习 01.初识 new Vue({ el: '#root', //用于指定当前Vue实例为哪个容器服务,值通常为css选择器符 data () { return { } } }); ...
- .NET面试题系列[1] - .NET框架基础知识(1)
很明显,CLS是CTS的一个子集,而且是最小的子集. - 张子阳 .NET框架基础知识(1) 参考资料: http://www.tracefact.net/CLR-and-Framework/DotN ...
- RabbitMQ基础知识
RabbitMQ基础知识 一.背景 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然 ...
- Java基础知识(壹)
写在前面的话 这篇博客,是很早之前自己的学习Java基础知识的,所记录的内容,仅仅是当时学习的一个总结随笔.现在分享出来,希望能帮助大家,如有不足的,希望大家支出. 后续会继续分享基础知识手记.希望能 ...
随机推荐
- MySQL 数据更新过程
文章转载自:https://mp.weixin.qq.com/s?__biz=MzI1MDgwNzQ1MQ==&mid=2247486441&idx=1&sn=fcf93709 ...
- 13. Fluentd输出插件:in_forward用法详解
in_forward插件通常用于从其他节点接收日志事件,这些节点包括其他Fluentd实例.fluent-cat命令行或者Fluentd客户端程序.这是目前效率最高的日志事件接收方法. in_forw ...
- HashMap底层原理及jdk1.8源码解读
一.前言 写在前面:小编码字收集资料花了一天的时间整理出来,对你有帮助一键三连走一波哈,谢谢啦!! HashMap在我们日常开发中可谓经常遇到,HashMap 源码和底层原理在现在面试中是必问的.所以 ...
- PAT (Basic Level) Practice 1010 一元多项式求导 分数 25
设计函数求一元多项式的导数.(注:xn(n为整数)的一阶导数为nxn−1.) 输入格式: 以指数递降方式输入多项式非零项系数和指数(绝对值均为不超过 1000 的整数).数字间以空格分隔. 输出格式: ...
- # 如何在Windows下运行Linux程序
如何在Windows下运行Linux程序 一.搭建 Linux 环境 1.1 安装 VMware Workstation https://www.aliyundrive.com/s/TvuMyFdTs ...
- 文件管理工具“三剑客” #Everything #SpaceSniffer #Clover
前言: 本文收集了我日常使用的三个文件管理工具: 文件搜索神器--Everything 磁盘文件占用分析工具--SpaceSniffer 文件资源管理器--Clover 下面我从工具解决的痛点和使用技 ...
- set 学习笔记
一.声明 1.头文件 \(include<set>//包括set和multiset两个容器\) 2.声明 \(set<int> s\) s自带一个维度 二.迭代器 对" ...
- vulnhub靶场之THE PLANETS: EARTH
准备: 攻击机:虚拟机kali.本机win10. 靶机:THE PLANETS: EARTH,网段地址我这里设置的桥接,所以与本机电脑在同一网段,下载地址:https://download.vulnh ...
- 使用ssm框架搭建的图书管理系统
等下贴代码 学生图书管理系统 实现的功能: 1.图书信息查询(暂时未分页处理) 2.图书信息修改(点击保存按钮后返回修改后的图书信息界面) 3.删除一本图书(删除某一本书籍,在图书管理界面则不存在该条 ...
- 齐博x1模型里边钩子的创建与使用
在模型里边的钩子创建与使用方法跟在控制器里边的钩子创建及使用方法是有所区别的在模型里边创建的钩子,你可以理解为执行一个函数,是无法调用模型里边的类的方法及属性的.比如系统文件\application\ ...