- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
大家好,我是山里看瓜,该系列文章是为了帮助大家不管面试还是开发对前端的一些基本但是很重要的知识点认识更加深入和全面.
想写这个系列文章的初衷是:我发现前端的很多基本知识,使用起来很简单,定义看起来也很简单。很多人你在问他相关问题的时候,他也能说上几句。但是为什么用?怎么用会更好?原理是什么?让你实现你怎么做?这些问题很多人都是一知半解,某些知识点我本人也是如此,只知道去用,甚至有时候都不知道为什么用,更别说原理,秉承的原则就是程序跟我要么有一个能跑,至于怎么跑那雨我无瓜... 。
本篇我们从各个方面来介绍Promise,把一些你知道的不知道的点全都梳理一遍,让你面试中讲得透彻,表现亮眼;让你在开发中使用更加知根知底.
我们将从以下几个方面进行讲解学习:
Promise
是什么?Promise
是一个代理,它代表一个在创建 promise
时不一定已知的值。它允许你将处理程序与异步操作的最终成功值或失败原因关联起来。这使得异步方法可以像同步方法一样返回值:异步方法不会立即返回最终值,而是返回一个 promise,以便在将来的某个时间点提供该值。Promise
是ES6规范提出的一个技术,用于在JavaScript中进行异步编程(读写文件、数据库、请求、定时器)的解决方案,原来的方案是使用回调嵌套的方式,容易造成回到地狱(后面我们会说到)。Promise
是一个构造函数Promise
对象用来封装一个异步操作并可以获取其成功或者失败的值Promise
的状态状态是 promise 实例对象中的一个属性:PromiseState,该属性有三种值:
状态变化有且只有两种情况:
pending
变为 resolved / fullfiled
。pending
变为 rejected
状态变化说明:
value
,执行失败的结果值一般称为 reason
。Promise
的结果Promise 的结果属性:
PromiseResult
这个属性,它保存着异步任务执行成功或者失败的结果。怎么修改 promise 的结果?
Promise
的工作流程Promise
?Promise
—— 方法参数详细说明Promise
构造器函数:Promise(executor) {}Promise
原型方法let p1 = Promise.resolve(520)
let p2 = Promise.resolve(new Promise((resolve, reject) => {
resolve('OK')
})) // 这时 p2 状态为成功,成功的值为 'OK'Ï
let p = Promise.reject(520) // 无论传入的是什么,返回的都是一个失败的promise 对象
// 传入什么,失败的结果就是什么
let p1 = new Promise((resolve, reject) => {
resolve('OK')
})
let p2 = Promise.resolve('Success')
let p3 = Promise.resolve('Success')
const result = Promise.all([p1, p2, p3])
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK')
}, 1000)
})
let p2 = Promise.resolve('Success')
let p3 = Promise.resolve('Success')
const result = Promise.race([p1, p2, p3]) // =>结果为 p2 的结果,因为p2 先改变状态
Promise
在开发中比较常用的技巧我们在实际开发中,经常遇到需要发多个请求获取数据,当这些请求之前并没有相互依赖时,我们使用正常的 Promise 方式去请求或者使用 async await方式请求,都是顺序执行,一个请求在前一个请求完成之后发起,这样非常的低效率,并且性能和体验都非常的差,我们依赖请求数据的页面部分会有长时间空白,用户体验非常差.
这时候我们可以使用 Promise.all() + async、await来同时并发请求,这样请求就可以同时发起,实现一个并行发出的效果.
Promise.all(promises) 方法的结果是一个包含所有异步操作的结果数组,能够一一对应上 promises 数组中的异步操作.
以下是一个简单示例:
// 请求接口数据的方法
const getApiData = async () {
const [res1, res2, res3] = await Promise.all(
[
Api.getData1(),
Api.getData2(),
Api.getData3(),
]
)
}
几个注意点:
await
时,函数必须使用 async 关键字;await
,给 Promise.all()
使用;Promise.all()
的结果是对应内部异步操作的数组,我们可以直接通过数组解构,获取每个请求的结果,方便后续针对请求值做操作。Promise
风格方法封装举例function mineReadFile (path) {
return new Promise((resolve, reject) => {
// 读取文件
require('fs').readFile(path, (err, data) => {
// 判断
if (err) reject(err)
// 成功
resolve(data)
})
})
}
// 调用
mineReadFile("/file/test.txt").then(value => {
console.log(value)
}, reason => {
console.log(reason)
});
function sendAJAX(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
// 设置响应数据格式
xhr.responseType = 'json'
xhr.open('GET', url)
xhr.send();
// 处理结果
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
// 判断成功
if (xhr.status >= 200 && xhr.status < 300) {
// 成功的结果
resolve(xhr.response)
} else {
reject(xhr.status)
}
}
}
})
}
// 调用
sendAJAX('https://api.apiopen.top/getJoke')
.then(value => {
console.log(value)
}, reason => {
console.warn(reason)
})
Promise
开始手写之前我们需要先搞清楚 promise 的几个关键问题:
如何改变 promise 的状态?
一个 promise 指定(then方法)多个成功/失败回调函数,都会调用吗?
let p = new Promise((resolve, reject) => {
resolve('ok') // 这里状态改变了,所以下边两个回调都会执行,如果状态不改变,下面的回调都不执行
})
// 指定回调 - 1
p.then(value => {
console.log(value)
})
// 指定回调 - 2
p.then(value => {
alert(value)
})
改变 promise 状态和指定回调函数的顺序是什么样的,谁先执行,谁后执行?
问题简单描述:promise 代码在运行时,resolve/reject改变状态先执行,还是 then 方法指定回调先执行?
都有可能,正常情况下是先指定回调再改变状态,但也可以先改变状态再指定回调 。
当执行器函数中的任务是一个同步任务(直接调 resolve()/reject()) 的时候,先改变 promise 状态,再去指定回调函数* 。
当执行器函数中的任务是一个异步任务的时候,then 方法先执行(指定回调),改变状态后执行 。
// 这时是hen 方法先执行(指定回调),改变状态后执行
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK')
}, 1000)
})
p.then(value => {
console.log(value)
})
如何先改状态再指定回调?
什么时候才能得到数据(回调函数什么时候执行)?
promise.then() 返回的新 promise 的结果状态有什么决定?
promise 如何串联多个操作任务?
promise异常穿透?
中断 promise 链 。
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK')
}, 1000)
})
p.then(value => {
console.log(111)
return new Promise(() => {})
}).then(value => {
console.log(222)
})
function Promise(executor) {
// 添加属性
this.PromiseState = 'pending'
this.PromiseResult = null
// 声明属性 因为实例对象不能直接调用onResolve跟onReject 所以下面then中需要先保存在callback里面
this.callbacks = []
// 保存实例对象的 this 的值
const self = this // 常见的变量名有self _this that
// resolve 函数
function resolve(data) {
// 判断状态
if (self.PromiseState !== 'pending') return
// console.log(this) => 这里的this指向window,下面用this的话时直接修改的window
// 1. 修改对象的状态 (PromiseState)
self.PromiseState = 'fulfilled'
// 2. 设置对象结果值 (PromiseResult)
self.PromiseResult = data
// 调用成功的回调函数
setTimeout(() => {
self.callbacks.forEach((item) => {
item.onResolved(data)
})
})
}
// reject 函数
function reject(data) {
// 判断状态
if (self.PromiseState !== 'pending') return
// 1. 修改对象的状态 (PromiseState)
self.PromiseState = 'rejected'
// 2. 设置对象结果值 (PromiseResult)
self.PromiseResult = data
// 调用失败的回调函数
setTimeout(() => {
self.callbacks.forEach((item) => {
item.onRejected(data)
})
})
}
try {
// 同步调用【执行器函数】
executor(resolve, reject)
} catch (e) {
// 修改 promise 对象状态
reject(e)
}
}
// 添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {
const self = this
// 判断回调函数参数
if (typeof onRejected !== 'function') {
onRejected = (reason) => {
throw reason
}
}
if (typeof onResolved !== 'function') {
onResolved = (value) => value
}
return new Promise((resolve, reject) => {
// 封装函数
function callback(type) {
try {
// 获取回调函数的执行结果
let result = type(self.PromiseResult)
// 判断
if (result instanceof Promise) {
result.then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
} else {
// 结果的对象状态为 【成功】
resolve(result)
}
} catch (e) {
reject(e)
}
}
// 调用回调函数 根据 PromiseState 去调用
if (this.PromiseState === 'fulfilled') {
setTimeout(() => {
callback(onResolved)
})
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callback(onRejected)
})
}
// 判断 pending 状态
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved: function () {
callback(onResolved)
},
onRejected: function () {
callback(onRejected)
},
})
}
})
}
// 添加 catch 方法
Promise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected)
}
// 添加 resolve 方法
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
} else {
// 状态设置为成功
resolve(value)
}
})
}
// 添加 reject 方法
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
// 添加 all 方法
Promise.all = function (promises) {
// 声明变量
let count = 0 // 计数
let arr = [] // 结果数组
// 遍历
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(v) => {
// 得知对象的状态是成功
// 每个promise对象成功都加 1
count++
// 将当前每个promise对象成功的结果都存入到数组中
arr[i] = v
// 判断
if (count === promises.length) {
// 修改状态
resolve(arr)
}
},
(r) => {
reject(r)
}
)
}
})
}
// 添加 race 方法
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (var i = 0; i < promises.length; i++) {
promises[i].then(
(v) => {
// 修改返回对象的状态为成功
resolve(v)
},
(r) => {
// 修改返回对象的状态为成功
reject(r)
}
)
}
})
}
// 封装成类
class Promise {
//构造方法
constructor(executor) {
// 添加属性
this.PromiseState = 'pending'
this.PromiseResult = null
// 声明属性 因为实例对象不能直接调用onResolve跟onReject 所以下面then中需要先保存在callback里面
this.callbacks = []
// 保存实例对象的 this 的值
const self = this // 常见的变量名有self _this that
// resolve 函数
function resolve(data) {
// 判断状态
if (self.PromiseState !== 'pending') return
// console.log(this) => 这里的this指向window,下面用this的话时直接修改的window
// 1. 修改对象的状态 (PromiseState)
self.PromiseState = 'fulfilled'
// 2. 设置对象结果值 (PromiseResult)
self.PromiseResult = data
// 调用成功的回调函数
setTimeout(() => {
self.callbacks.forEach((item) => {
item.onResolved(data)
})
})
}
// reject 函数
function reject(data) {
// 判断状态
if (self.PromiseState !== 'pending') return
// 1. 修改对象的状态 (PromiseState)
self.PromiseState = 'rejected'
// 2. 设置对象结果值 (PromiseResult)
self.PromiseResult = data
// 调用失败的回调函数
setTimeout(() => {
self.callbacks.forEach((item) => {
item.onRejected(data)
})
})
}
try {
// 同步调用【执行器函数】
executor(resolve, reject)
} catch (e) {
// 修改 promise 对象状态
reject(e)
}
}
// then 方法封装
then(onResolved, onRejected) {
const self = this
// 判断回调函数参数
if (typeof onRejected !== 'function') {
onRejected = (reason) => {
throw reason
}
}
if (typeof onResolved !== 'function') {
onResolved = (value) => value
}
return new Promise((resolve, reject) => {
// 封装函数
function callback(type) {
try {
// 获取回调函数的执行结果
let result = type(self.PromiseResult)
// 判断
if (result instanceof Promise) {
result.then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
} else {
// 结果的对象状态为 【成功】
resolve(result)
}
} catch (e) {
reject(e)
}
}
// 调用回调函数 根据 PromiseState 去调用
if (this.PromiseState === 'fulfilled') {
setTimeout(() => {
callback(onResolved)
})
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callback(onRejected)
})
}
// 判断 pending 状态
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved: function () {
callback(onResolved)
},
onRejected: function () {
callback(onRejected)
},
})
}
})
}
// catch 方法
catch(onRejected) {
return this.then(undefined, onRejected)
}
// resolve 方法
static resolve(value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
} else {
// 状态设置为成功
resolve(value)
}
})
}
// reject 方法
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
// all 方法
static all(promises) {
// 声明变量
let count = 0 // 计数
let arr = [] // 结果数组
// 遍历
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(v) => {
// 得知对象的状态是成功
// 每个promise对象成功都加 1
count++
// 将当前每个promise对象成功的结果都存入到数组中
arr[i] = v
// 判断
if (count === promises.length) {
// 修改状态
resolve(arr)
}
},
(r) => {
reject(r)
}
)
}
})
}
//race 方法
static race(promises) {
return new Promise((resolve, reject) => {
for (var i = 0; i < promises.length; i++) {
promises[i].then(
(v) => {
// 修改返回对象的状态为成功
resolve(v)
},
(r) => {
// 修改返回对象的状态为成功
reject(r)
}
)
}
})
}
}
async
和await
// resource 1.html 2.html 3.html
const fs = require('fs')
// 回调函数的方式
fs.readFile('./resousrce/1.html', (err, data1) => {
if (err) throw err
fs.readFile('./resousrce/2.html', (err, data2) => {
if (err) throw err
fs.readFile('./resousrce/3.html', (err, data3) => {
if (err) throw err
console.log(data1 + data2 + data3)
})
})
})
// resource 1.html 2.html 3.html
const fs = require('fs')
const util = require('util')
const mineReadFile = util.pomiseify(fs.readFile)
// async 与 await 结合
async function main() {
try {
// 读取第一个文件的内容
let data1 = await mineReadFile('./resourse/1.html')
let data2 = await mineReadFile('./resourse/2.html')
let data3 = await mineReadFile('./resourse/3.html')
console.log(data1 + data2 + data3)
}catch(e) {
console.log(e)
}
}
main()
// async 与 await 结合发送 Ajax 请求
function sendAJAX(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
// 设置响应数据格式
xhr.responseType = 'json'
xhr.open('GET', url)
xhr.send();
// 处理结果
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
// 判断成功
if (xhr.status >= 200 && xhr.status < 300) {
// 成功的结果
resolve(xhr.response)
} else {
reject(xhr.status)
}
}
}
})
}
// 段子接口地址:https://api.apiopen.top/getJoke
let btn = document.querySelector('#btn')
btn.addEventListener('click', async function () {
// 获取段子信息
let duanzi = await sendAJAX('https://api.apiopen.top/getJoke')
console.log(duanzi)
})
前端的东西其实很多并不难,只是很多人很少去深究,去全面了解,大家都只是学个大概,会用就行; 。
本系列文章将会全面深入的带你重新夯实前端基础,把一些重要且常用的知识点深入讲解; 。
希望看完文章的你能有所收货,使用起来更加轻松,面试更加自如亮眼; 。
我相信能看到这里的人呢,都是想进步想成长的小伙伴,希望在工作小伙伴的升职加薪,在找工作的小伙伴面试顺利,收割offer; 。
对你有帮助的话给作者点点关注吧,你的支持就是我创作的动力!Peace and love~~ 。
不知道有没有喜欢说唱的小伙伴,作者有个想法,每期文章最后分享一首觉得不错的说唱,当然你觉得好听的歌曲也可以评论区分享给大家.
本期歌曲:《ghost face》—— 法老 。
最后此篇关于前端基本功——面试必问系列(1):都2024了,还没吃透Promise?一文搞懂的文章就讲到这里了,如果你想了解更多关于前端基本功——面试必问系列(1):都2024了,还没吃透Promise?一文搞懂的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
1. HTML 结构 1.1 HTML 文件基本结构 第一个html程序 hello world! html 标签是整个 h
文件上传三种方案:1. form表单上传,2. iframe,3. FormData,base64上传文件,二进制流上传文件,二进制流下载文件。异步上传,大文件上传--切片:拆分上传请求断点续传显示上
1. HTML 结构 1.1 HTML 文件基本结构 第一个html程序 hello world! html 标签是整个 h
uniapp作为开发移动端的前端框架,目前国内是非常流行的,使用HbuilderX开发工具基于uniapp框架开发的系统可以方便的转换为小程序、APP等移动端程序,大大降低了移动开发的成本。网络
今天我们来复盘一下前端中css伪元素的知识以及如何用css伪元素来减轻javascript的压力,做出一些脑洞大开的图形。 预备知识 伪元素 伪元素是一个附加至选择器末的
今天给大家分享一些实用的JS代码片段,有需要的朋友欢迎收藏! 1、获取浏览器的版 functiongetBrowser(){ varUserAgent=navigator.us
1 . Wappalyzer 全球用户数:1,000,000+ Wappalyzer可以帮助我们了解目标网站的构建方式。工作当中存在大量此类情况,客户需要我们参照某些网站
在管理后台中我们会使用大量的表格表单组件, 导入导出各种报表, 有些场景还需要对报表数据进行可视化分析, 动态生成可视化图表, 笔者将基于以上场景, 总结一些实用的 Table 组件开发技巧,
3D动画效果现在越来越普及,已经被广泛的应用到了各个平台,比如阿里云,华为云,webpack官网等。它可以更接近于真实的展示我们的产品和介绍,带来极强的视觉冲击感。所以说,为了让自己更加优秀,c
QShop商城-快速开始-前端 工具准备 NodeJs 前端环境为NodeJs,下载地址:http://nodejs.cn/download/current/ 。 默认会用版
1. 初始JavaScript 1.1 什么是 JavaScript JavaScript (简称 JS) 是世界上最流行的编程语言之一 是一个脚本语言, 通过解释器运行 主要在客户端(浏览器)上运行
1. WebAPI 背景知识 1.1 什么是 WebAPI JS 分成三个大的部分: ECMAScript: 基础语法部分 DOM API: 操作页面结构 BOM API: 操作浏览器 WebAPI
1. WebAPI 背景知识 1.1 什么是 WebAPI JS 分成三个大的部分: ECMAScript: 基础语法部分 DOM API: 操作页面结构 BOM API: 操作浏览器 WebAPI
1. 初始JavaScript 1.1 什么是 JavaScript JavaScript (简称 JS) 是世界上最流行的编程语言之一 是一个脚本语言, 通过解释器运行 主要在客户端(浏览器)上运行
有没有办法从页面访问 tomcat 服务器日志?如果有一些方法或实现可以做到这一点...... 最佳答案 PSI Probe可以列出您的 Tomcat 日志文件并显示它们的内容。您可以采用相同的方法,
我想知道是否有一些很好的免费网站性能分析工具,特别是前端。这主要是关于Javascript的。 现有工具(例如 Google Pagespeed)的问题在于它不适用于我的应用程序。在进入我的应用程序之
我曾经遇到一个 MySQL 前端应用程序,它在父行中显示外部链接行,例如,如果 Client 表有一个指向 Suburb 表的外键: (来源:vb123.com) 您知道可以执行此操作的任何前端吗?
我正在建立一个带有管理区域的网上商店来管理产品。在管理区域中,所有产品都是可见的,但在网上商店中,只有数据库表中标记为 active = 1 的产品是可见的。 我正在使用 Silex 并将存储库注册为
有可能在 C# 中制作 GUI,但在 C 或 C++ 中制作实际程序。 比如说我想制作一个聊天应用程序。我希望界面在 C# 中。但我想用 C 编写所有实际代码。这可能吗? 我找到了 http://ww
对于我自己的教育,我很好奇编译器使用哪个 C++ 前端和后端。您能告诉我以下技术在哪里使用以及它们有哪些标志/优势(如果有的话)? Open64 - 它是后端、前端还是两者兼而有之?哪些编译器使用它?
我是一名优秀的程序员,十分优秀!