文章目录
Promise是什么
promise是ES6的一门新技术,是JS中处理异步编程
的解决方法
,旧的方法是单纯地使用回调函数
。
异步编程
步编程是一种使用非阻塞性函数
或者非顺序执行逻辑
的编程方式,通过使用异步编程,可以避免因为等待慢操作(如读写文件、访问数据库或网络请求)而浪费CPU的时间,提高程序执行效率。
在同步编程中,代码会按照从上到下的顺序依次执行,每一行代码都必须等待上一行代码执行完毕之后才能执行。这种顺序执行的方式很容易理解,也很容易编写代码,但是有个明显的缺点:无法充分利用计算机资源,当遇到需要大量时间的操作时(如I/O操作),CPU就会处于空闲状态。
而异步编程则可以解决这个问题。在异步编程中,函数的调用不会阻塞后续操作的执行。当异步操作(如I/O操作)被调用后,程序不会等待其结果,而是立即执行后续的代码。当异步操作完成后,通过事件或者回调函数来处理结果。
常见的异步编程
-
fs文件操作:
通过 fs 模块的函数例如 fs.readFile 或 fs.writeFile 读写文件都是异步操作。require('fs').readFile('./imdex.html', (err,data)=>{回调函数})
-
数据库操作:例如查询数据库,这种 I/O 操作通常都是异步的。
-
AJAX请求:使用 fetch API 或者 XMLHttpRequest 发送的 HTTP 请求,他们不会阻塞后续代码的执行
$.get('/server', (data)=>{回调函数})
-
定时操作:setTimeout 和 setInterval
setTimeOut(()=>{回调函数}, 2000)
-
用户操作:如按钮点击、键盘输入等用户接口事件,对这些事件的响应处理也是异步操作。
-
Promise
任何返回 Promise 的函数或方法都是异步的。const p = new Promise((reslove, reject) => { reslove() }).then( () => { // 成功回调 alert("恭喜,中奖啦"); }, () => { // 失败回调 alert("再接再厉"); } );
从语法上来说: Promise是一个
构造函数
,是一种异步编程的方式
从功能上来说: promise对象用来封装一个异步操作
并可以获取其成功/失败的结果值,可以处理异步编程的返回结果- Web Workers:多线程机制也是一种异步操作。 Web Workers 是一种让 Web 内容在后台线程运行脚本的方法。
- WebSockets 和 Server-Sent Events:这两种技术都用于服务器和客户端之间的双向通信,事件驱动的处理方式也是异步的。
常规逻辑下,异步编程执行完之后我们需要根据异步编程的结果处理一些内容,称之为处理异步编程的解决方法,常见的处理方法有回调函数
,还有Promise
。
Promise的优势
-
支持链式调用,解决
回调地狱
问题。
回调地狱:多个回调函数嵌套调用以达到函数异步调用
的效果
通过嵌套调用,函数1执行后才能执行函数2,函数2执行后才能执行函数3…
这样肃然达到了异步调用的效果,却引来一个问题:函数嵌套的层级太深,代码不断向前缩进,不便于阅读,不便于异常处理。
eg:setTimeout(()=>{ console.log("回调函数1") setTimeout(()=>{ console.log("回调函数2") setTimeout(()=>{ console.log("回调函数3") },1000) },1000) }, 1000)
可以替换为
async function Fun1() { await new Promise(resolve=>setTimeout(()=>{ console.log("回调函数1");resolve()}, 1000)) await new Promise(resolve=>setTimeout(()=>{ console.log("回调函数2");resolve()}, 1000)) await new Promise(resolve=>setTimeout(()=>{ console.log("回调函数3");resolve()}, 1000)) } Fun1()
-
Promise指定回调函数的方式更加灵活
旧的:必须在启动异步任务前指定回调函数
Promise:启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数(可以有多个, .then方法)
Promise使用案例
抽奖案例
案例:抽奖案例,点击按钮进行抽奖,将抽检结果进行弹窗显示。
-
普通实现:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div class="container"> <h2 class="page-header">Promise初体验</h2> <button id="btn" class="btn">点击抽奖</button> </div> <script> // 随机数 function rand(m, n) { return Math.ceil(Math.random() * (n - m + 1)) + m - 1; } const btn = document.querySelector("#btn"); btn.addEventListener("click", () => { // setTimeout实现 setTimeout(() => { // 30%中奖概率 const n = rand(1, 100); console.log("n", n); if (n <= 30) { alert("恭喜,中奖啦"); } else { alert("再接再厉"); } }, 1000); }); </script> </body> </html>
-
Promise实现:
(Promise是一个构造函数,实例化的参数是两个函数reslove和reject,reslove是成功时调用的函数,reject是失败时调用的函数,实例化函数里面存放异步任务
)
(实例化后的Promise有.then
方法,该方法有两个参数,第一个是Promise调用reslove后执行的结果,第二个是Promise调用reject后执行的结果 )
(如果在调用reslove和reject时进行了参数传递,他们的参数分别会传递个.then函数的第一个回调函数和第二个回调函数)<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div class="container"> <h2 class="page-header">Promise初体验</h2> <button id="btn" class="btn">点击抽奖</button> </div> <script> // 随机数 function rand(m, n) { return Math.ceil(Math.random() * (n - m + 1)) + m - 1; } const btn = document.querySelector("#btn"); btn.addEventListener("click", () => { // promise实现 /* reslove, reject都是函数参数 当异步任务成功掉reslove,失败掉reject */ const p = new Promise((reslove, reject) => { //包裹异步操作,对异步任务进行封装 setTimeout(() => { // 30%中奖概率 const n = rand(1, 100); if (n <= 30) { // 将Promise的状态设置为成功 reslove(); } else { // 将Promise的状态设置为失败 reject(); } }, 1000); }).then( () => { // 成功回调 alert("恭喜,中奖啦"); }, () => { // 失败回调 alert("再接再厉"); } ); }); </script> </body> </html>
fs案例
使用Promise读取文件操作
const fs = require('fs')
// 回调函数形式
fs.readFile('./public/content.txt',(err,data)=>{
if(err){
throw(err)
}else{
// 直接读取data是一个Buffer格式的数据,<Buffer 48 65 6c 6c 6f ef bc 8c e6 88 91 e6 98 af 63 6f 6e
console.log('context文件内容:', data);
// 使用toString方法可以转变成可读文件
console.log('context文件内容:', data.toString());
}
})
// Promise形式
let p = new Promise((reslove, reject)=>{
fs.readFile('./public/content.txt',(err,data)=>{
if(err){
reject(err)
}else{
reslove(data)
}
})
}).then((value)=>{
// 使用toString方法可以转变成可读文件
console.log('context文件内容:', value.toString());
},(reason)=>{
console.log(reason);
})
ajax案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div class="container">
<h2 class="page-header">Promise 封装 Ajax</h2>
<button id="btn" class="btn">点击发送AJAX</button>
</div>
<script>
const btn = document.querySelector("#btn");
// AJAX实现
btn.addEventListener("click", () => {
// 创建对象
const xhr = new XMLHttpRequest();
// 初始化
xhr.open("GET", "https://api.apiopen.top/getJoke");
// 发送
xhr.send();
// 处理响应结果
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
// 控制台输出响应体
console.log("成功", xhr.status, xhr.response);
} else {
// 控制台输出响应状态码
console.log("失败", xhr.status);
}
}
};
});
// Promise实现
btn.addEventListener("click", () => {
new Promise((reslove, reject) => {
// 创建对象
const xhr = new XMLHttpRequest();
// 初始化
xhr.open("GET", "https://api.apiopen.top/getJoke");
// 发送
xhr.send();
// 处理响应结果
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
reslove(xhr.response);
} else {
reject(xhr.status);
}
}
};
}).then(
(value) => {
console.log("成功", value);
},
(reason) => {
console.log("失败", reason);
}
);
});
</script>
</body>
</html>
Promise封装,fs封装
封装一个函数mineReadFile读取文件内容。
const { resolve } = require('path')
function mineReadFile(path) {
return new Promise((resolve, reject) => {
// 读取文件
require('fs').readFile(path, (err, data) => {
if (err) reject()
resolve(data)
})
})
}
mineReadFile('./resource/content.txt').then((value) => {
console.log("文件内容是", value.toString());
}, (reason) => {
console.log(reason);
})
Promise封装,ajax封装
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
/*
封装一个函数sendAjax发送GET AJAX请求
参数 URL,请求地址
返回结果 Promise对象
*/
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(zhr.status);
}
}
};
});
}
sendAjax("https://api.apiopen.top/getJoke").then(
(value) => {
console.log("结果", value);
},
(reason) => {
console.log(reason);
}
);
</script>
</body>
</html>
上述案例就是对ajax的Promise封装,封装后的函数只需要输入请求地址,请求结果使用 .then
进行接收。我们常用的axios发送ajax请求,就是对ajax的Promise封装。
util.promisify方法
util.promisify 方法:
- 接受一个参数是一个函数,且该函数满足:最后一个参数是
错误优先的回调函数
, 即(err,value)=>{}
格式 - 返回结果是一个promise对象
/**
* util.promisify 方法:
* 接受一个参数是函数, 且该最后一个参数是(err,value)=>{}格式的函数
* 返回结果是一个promise对象
*/
// require('fs').readFile(path, (err, value)=>{})
let mineReadFile = require('util').promisify(require('fs').readFile)
mineReadFile('./resource/content.txt').then(value => {
console.log('文件内容是:', value.toString());
})
util.promisify最常用的情形: 将普通的回调函数形式的异步编程 封装成 Promise形式的异步编程
.
其实util.promisify的原理就是对回调函输进行Promise封装,类似于上述按案例 Promise封装,fs封装
.
Promise的状态
Promise的状态是什么
Promise状态是Promise实例对象身上的一个属性PromiseState
.
输出一个Promise对象如下:
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: null
Promise的三种状态
属性PromiseState
的取值有三种:
- pending 未决定的 (初始状态)
- resolved / fullfilled 成功
- rejected 失败
PromiseState
的值是内置的我们不能直接进行操作。
Promise的状态改变
promise的状态改变只可能有两种情况
- pending变为resolved
- pending变为rejected
有且只有这两种情况,且一个Promise对象的状态只能改变一次
,无论变为成功还是失败,都会有一个结果数据,成功的结果数据一般称为value,失败的结果数据一般称为reason。
Promise的结果
Promise的结果是什么
Promise结果是Promise实例对象身上的另一个属性PromiseResult
,保存的是Promise对象成功或失败的结果。
输出一个Promise对象如下:
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: null
Promise的结果的设置
Promise的结果的设置是通过Promise的 resolve(value)
和 reject(value)
两个函数的参数设置的,他们传递的是什么参数 Promise的结果值(PromiseResult)就是什么。
Promise的API
Promise的构造函数
Promise对象的创建是通过Promise的构造函数创建的。new Promise((resolve, reject)=>{})
。
(resolve, reject)=>{}
也叫做执行器函数,resolve和reject是两个函数:resolve函数是成功调用的函数;reject函数是失败调用的函数。
重要:执行器(resolve, reject)=>{}中的代码是同步的,不是异步的
,所谓同步是指(resolve, reject)=>{}
中代码的执行时机和Promise外的代码是同步执行的。
console.log(1);
new Promise((resolve, reject) => {
// 这里的代码是同步调用的
console.log(2);
});
console.log(3);
输出结果: 123
Promise.prototype.then方法
就是Promise的.then
方法,他是Promise原型上的一个方法:.then((onResolved, onReject) =>{})
- onResolved函数:成功时调用的回调函数,Promise中使用resolve方法对应的函数。
- onReject函数:失败时调用的回调函数,Promise中使用reject方法对应的函数。
重要:.then方法的返回值仍然是一个Promise对象,可以接着调用.then
方法,这就是我们所说的链式调用
Promise.prototype.catch方法
指定的是Promise的失败的回调,.catch((onReject)=>{})
,Promise中使用reject方法时就会调用catch
中的回调。
eg:
new Promise((resolve, reject) => {
reject("error");
}).catch((error) => {
console.log(error);
});
Promise.reslove方法
注意该方法是Promise函数对象上的方法,不是实例对象身上的方法,上面说的.then和.catch方法是实例对象身上的方法。
Promise.reslove((value)=>{})
:
value是成功的数据或Promise对象,并且该函数返回的是一个成功/失败Promise对象
。
eg:
console.log(Promise.resolve(521));
输出:
传递的value参数和返回结果的关系:
- 如果传入的参数为非Promise类型的对象,则返回的结果为成功promise对象
- 如果传入的参数为 Promise对象,则参数的结果决定了返回的结果。如果参数是成功的Promise对象,那么结果就是成功的Promise对象;如果参数是失败的Promise对象,那么结果就是失败的Promise对象。
注意 :和Promise执行器中的reslove方法不一样,Promise执行器中的reslove方法返回值是undefine
new Promise((resolve, reject) => {
console.log(resolve("success"));
}).catch((error) => {
console.log(error);
});
输出:
Promise.reject方法
注意该方法是Promise函数对象上的方法。
Promise.reject((reason)=>{})
:
reason是失败的原因,并且该函数返回的是一个失败的Promise对象
。失败的结果是传入的值。
eg:
console.log(Promise.reject(521));
console.log(
Promise.reject(
new Promise((resolve, reject) => {
resolve("success");
})
)
);
Promise.all方法
Promise.all((promiseArray)=>{})
,promiseArray表示的是多个Promise对象,Promise组成的数组。
Promise.all
返回的是一个新的promise:只有所有的promise都成功返回的Promise对象的状态才是成功,值是成功的promise的结果组成的数组;只要有一个失败了返回的Promise对象的状态就是失败,值是第一个失败的promise的结果。
注意: promiseArray中的内容是一起执行的不是顺次执行的。
eg:
let p1 = new Promise((resolve, reject) => {
resolve("ok");
});
let p2 = Promise.resolve("Success");
let p3 = Promise.resolve("on Yeah");
const result = Promise.all([p1, p2, p3]);
console.log(result);
结果:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
let p1 = new Promise((resolve, reject) => {
resolve("ok");
});
let p2 = Promise.reject("Error");
let p3 = Promise.reject("on Yeah");
const result = Promise.all([p1, p2, p3]);
console.log(result);
</script>
</body>
</html>
结果:
实战:使用Promise.all
Promise.all使用
const deleteFunList:Array<()=>void> = []
// 异步函数
const deleteFun = (region) => {
return () => {
// 一定将axios请求进行返回
return axiosApi.deleteConfig({
id: detailValue.value && detailValue.value.id,
region
})
}
}
const originRegionList=['ar','cn','am','ai']
originRegionList.forEach(item => {
if (nowRegionList.indexOf(item) === -1) {
deleteFunList.push(deleteFun(item))
}
})
await Promise.all(deleteFunList.map(item => item()))
axios请求
deleteConfig(data) {
return new Promise((resolve, reject) => {
setTimeout(async () => {
await axios({
url: API.deleteConfig,
method: 'post',
data
}).then(res => res.data)
.then(data => {
resolve(data)
}).catch(error => {
reject(error)
})
}, 3000)
})
}
需要注意的点:
- deleteFun 返回的箭头函数仍然需要有返回值,返回之后Promise.all才能接收到参数
- axios模拟延迟请求的时候不能只用
setTimeout
还需要使用new Promise
包裹:
return new Promise((resolve, reject) => {
setTimeout(async () => {
await axios({})
})
}
Promise.race方法
Promise.race((promiseArray)=>{})
,promiseArray表示的是多个Promise对象,Promise组成的数组。
Promise.all
返回的是一个新的promise:promiseArray中第一个完成的
Promise的结果状态就是返回的promise的状态。
eg:
let p1 = new Promise((resolve, reject) => {
resolve("ok");
});
let p2 = Promise.reject("Success");
let p3 = Promise.resolve("on Yeah");
const result = Promise.race([p1, p2, p3]);
P1执行最快:
Promise几个关键问题
如何改变Promise的状态
- 调用
resolve
函数,调用Promise函数将状态从Pending
转换成fulfilled
- 调用
reject
函数,调用Promise函数将状态从Pending
转换成rejected
- 抛出错误,调用
throw
,调用Promise函数将状态从Pending
转换成rejected
Promise指定多个成功和失败的回调函数(链式调用.then),是否都会调用
当Promise改变为对应状态时都会调用,链式调用。
改变Promise的状态和指定回调函数
谁先谁后
都有可能:
- 正常情况下是先指定回调函数(回调函数指的是
.then
中的回调函数,指定回调函数的意思是进行回调函数的加载)再修改状态。 - 但是也可能先改变状态再指定回调函数。
(1) 先指定回调函数再修改状态
- Promise中的函数时
异步任务
时
let p = new Promise((reslove, reject) => {
setTimeout(() => {
reslove("OK");
}, 1000);
});
p.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
(2) 先改变状态再指定回调函数
- 在执行器中直接调用
reslove/ reject
函数
let p = new Promise((reslove, reject) => {
reslove("OK");
});
p.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
- 延迟
.then()
方法的执行
let p = new Promise((reslove, reject) => {
setTimeout(() => {
reslove("OK");
}, 1000);
});
setTimeout(() => {
p.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
}, 2000);
then回调函数什么时候执行
Promise的状态发生变化后就会调用then回调函数
,Promise执行器函数中既可以是同步函数
也可以是异步函数
,then回调函数在Promise执行器中的函数执行完之后再执行。
then方法返回结果的特点
.then
方法的返回结果是一个Promise对象,后面可以继续调用.then
方法。
.then
方法的返回的Promise对象的状态由then()方法指定的回调函数
的返回结果决定:
- 如果抛出异常(throw),
.then
方法的返回的Promise对象的状态是reject, 值为throw抛出的内容 - 如果返回的是非promise的任意值,
.then
方法的返回的Promise对象的状态为resolved, 值为返回的内容 - 如果返回的是另一个新promise,此promise的结果就会成为
.then
方法的返回的Promise对象的结果 - 注意: 如果没有返回结果,默认返回结果就是 undefined,undefined不是Promise对象,所以返回结果就是状态为resolved, 值为undefined的Promise对象
let p = new Promise((resolve, reject) => {
resolve("ok");
});
let result = p.then(
(value) => {
// console.log(value);
// 1. 抛出错误
// throw "出错了";
// 2. 返回结果是非Promise类型的对象
// return 521;
// 3. 返回的是Promise对象
return new Promise((resolve, reject) => {
reject("error");
});
},
(reason) => {
console.err(reason);
}
);
console.log("result", result);
返回结果:
- 抛出错误
- 返回结果是非Promise对象
- 返回结果是Promise对象
Promise如何串联多个操作任务
(1) promise 的 then()返回一个新的promise
,可以开成then()的链式调用
(2) 通过then 的链式调用串连
多个同步/异步任务
eg:
new Promise((resolve, reject) => {
// 异步任务1
setTimeout(() => {
resolve("OK");
}, 1000);
})
.then((value) => {
// 异步任务2
return Promise((resolve, reject) => {
resolve("success");
});
})
.then((value) => {
console.log(value);
});
隔1s后返回:
Promise的异常穿透
Promise的异常穿透是指可以在链式调用的最后指定失败的回调
(.then的第二个参数或者catch方法)。
最后的失败回调函数可以捕获该链式调用中的所有失败情况。
eg:
new Promise((resolve, reject) => {
setTimeout(() => {
reject("error");
}, 1000);
})
.then((value) => {
console.log(111);
})
.then((value) => {
console.log(222);
})
.then((value) => {
console.log(333);
})
.catch((reason) => {
console.warn(reason);
});
输出:
中断Promise链
Promise链指的是Promise的链式调用,有且仅有一种方法可以中断Promise的链式调用:返回pending状态的Promise对象
(eg:return new Promise(()=>{}))。
原理:返回pending状态的Promise,就是Promise的状态没有改变,状态没有改变,就不会继续向下执行。
new Promise((resolve, reject) => {
setTimeout(() => {
reject("error");
}, 1000);
})
.then((value) => {
console.log(111);
return new Promise(()=>{})
})
.then((value) => {
console.log(222);
})
.then((value) => {
console.log(333);
})
.catch((reason) => {
console.warn(reason);
});
输出:
自定义(手写)Promise
- 基本结构
- 构造函数有一个参数
- 有.then方法,在原型上需要添加.then方法,并且有两个参数
- 实现:
Promise.js
class Promise{
// 构造方法
constructor(executor) {
// 属性
this.PromiseState = 'pending'
this.PromiseResult = null
// 存储then
this.callback = []
const self = this
// resolve函数
function resolve(data) {
// 判断,确保状态只能被修改一次
if (self.PromiseState !== 'pending') return;
// 修改对象的状态(PromiseState)
// 直接使用this.PromiseState,this指向的是windowa,而不是实例对象
self.PromiseState = 'fulfilled'
// 修改对象的结果(PromiseResult)
self.PromiseResult = data
// 调用then方法
setTimeout(() => {
self.callback.forEach((item) => {
if (item.onResolved) {
item.onResolved(data)
}
})
})
}
// reject函数
function reject(data) {
// 判断,确保状态只能被修改一次
if (self.PromiseState !== 'pending') return;
// 修改对象的状态(PromiseState)
self.PromiseState = 'reject'
// 修改对象的结果(PromiseResult)
self.PromiseResult = data
// 调用then方法
setTimeout(() => {
self.callback.forEach((item) => {
if (item.onReject) {
item.onReject(data)
}
})
})
}
// 捕获异常(throw抛出的错误)
try {
// 同步调用的 执行器函数
executor(resolve, reject)
} catch (e) {
// 修改状态为失败
reject(e)
}
}
// then方法
// 添加then方法
then(onResolved, onReject) {
const self = this
// 处理回调函数的参数
if (typeof onResolved !== 'function') {
onResolved = value => value
}
if (typeof onReject !== 'function') {
onReject = reason => {
throw reason
}
}
// 返回Promise状态的对象
return new Promise((resolve, reject) => {
// 结果处理函数
function resultCallback(typeFunction) {
try {
// 获取回调函数的执行结果
let result = typeFunction(self.PromiseState)
if (result instanceof Promise) {
// 如果是Promise对象的话,直接返回Promise
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
// 不是Promise对象的话,结果的对象状态为成功
resolve(result)
}
} catch (e) {
reject(e)
}
}
// 成功的回调(then方法是Promise实例对象调用的,所以this指向的是实例对象)
if (this.PromiseState === "fulfilled") {
setTimeout(() => {
resultCallback(onResolved)
})
}
// 失败的回调
if (this.PromiseState === "rejected") {
setTimeout(() => {
resultCallback(onReject)
})
}
// pending的回调(保存回调函数,暂不执行)
if (this.PromiseState === "pending") {
// 保存回调函数
this.callback.push({
onResolved: function () {
resultCallback(onResolved)
},
onReject: function () {
resultCallback(onReject)
},
})
}
})
}
// catch方法
catch(onReject) {
return this.then(undefined, onReject)
}
// resolve方法,类上的方法
static resolve(value) {
// 返回Promise对象
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
// 如果是Promise对象的话,直接返回Promise
value.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
// 不是Promise对象的话,结果的对象状态为成功
resolve(value)
}
})
}
static reject(reason){
return new Promise((resolve, reject) => {
reject(reason)
})
}
static all(promises) {
// 返回结果是Promise对象
return new Promise((resolve, reject) => {
// 遍历
let count = 0;
let arr = []
for (let i = 0; i < promises.lengthy; i++){
promises[i].then(v => {
// 三个Promise对象都成功才调用
count++
// 将当前Promise对象成功的结果存入到数组中
arr[i] = v
if (count === promises.length) {
resolve(arr)
}
}, r => {
reject(r)
})
}
})
}
static race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++){
promises[i].then(v => {
resolve(v)
}, r => {
reject(r)
})
}
})
}
}
- 使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="./promise.js"></script>
</head>
<body>
<script>
// 这里的Promise是 ./promise.js 文件中的Promise
let p = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve("ok");
reject("error");
}, 1000);
});
console.log(p);
const result = p
.then((value) => {
console.log(value);
})
.then((value) => {
console.log(value);
})
.then((value) => {
console.log(value);
});
console.log("result", result);
</script>
</body>
</html>
Async函数
-
使用
async修饰的函数
的返回值是Promise对象
-
而promise对象的结果由async函数执行的返回值决定:(和.then一样)
- 如果返回值是非Promise类型的对象,那async函数的返回结果就是一个成功的Promise对象
- 如果返回的是一个Promsie类型的对象, 那async函数的返回结果和当前Promise对象一致
- 如果抛出异常,那async函数的返回结果是一个失败的Promise对象,结果是抛出的内容
-
如果没有返回值并且async函数中没有await,它也会隐式地返回一个 Promise 对象。这个 Promise 对象的状态取决于函数执行的结果:
- 如果函数执行过程中没有发生错误,Promise 对象的状态为 fulfilled,并且其值是 undefined。
- 如果函数执行过程中发生错误,Promise 对象的状态为 rejected,并且其值是错误对象。
-
如果没有返回值但是async函数中有await,它也会隐式地返回一个 Promise 对象, Promise 对象的结果取决于 await 表达式的结果。
- 如果 await 表达式成功执行,并且没有发生错误,那么 async 函数隐式返回的 Promise 对象的状态为 fulfilled,其值就是 await 表达式的结果。
- 如果 await 表达式执行过程中发生错误,那么 async 函数隐式返回的 Promise 对象的状态为 rejected,其值就是错误对象。
eg:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
async function main() {
// 如果返回值是非Promise类型的对象,那async函数的返回结果就是一个成功的Promise对象
return 123;
// 如果返回的是一个Promsie类型的对象, 那async函数的返回结果和当前Promise对象一致
return new Promise((resolve, reject) => {
resolve("ok");
});
// 如果抛出异常,那async函数的返回结果是一个失败的Promise对象,结果是抛出的内容
throw "Oh No";
}
let result = main();
console.log(result);
</script>
</body>
</html>
Await函数
- await右侧的表达式一般为 Promise对象,但也可以是其它的值(如基本数据类型等)
- 如果表达式是Promise对象, await返回的是Promise
成功的值
。(如果promise是失败的应该被catch
捕获, await函数一般被try…catch…包裹) - 如果表达式是其它值,直接将此值作为 await的返回值
async和await
async和await的关系
await必须写在async,函数中,但async函数中可以没有await
- 如果 await的
promise失败
了,就会抛出异常,需要通过try...catch
捕获处理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
async function test() {
let p = new Promise((resolve, reject) => {
reject("ok");
});
// 右侧是Promise的情况
try {
await p;
} catch (e) {
console.log(e);
}
}
</script>
</body>
</html>
async和await的结合使用
async和await可以以同步的方式实现异步代码
。简单地可以理解为await可以等待异步代码的执行,异步代码执行完之后再继续向下执行。但是实现以同步的方式实现异步代码
的前提是await后面的函数返回值是Promise类型
。
在async和await之前是使用回调函数的方式实现异步代码,回调函数嵌套回调函数:
()=>{
fs.readFile('./1.html', (err, data)=>{
fs.readFile('./2.html', (err, data)=>{
……
)
)
}
上述代码就会先读取完1.html再读取2.html
使用async和await:(fs.readFile返回值是Promise)
async()=>{
await fs.readFile('./1.html')
await fs.readFile('./2.html')
}
上述代码也会先读取完1.html再读取2.html
常见的应用场景
- 读取文件
const { log } = require('console')
const fs = require('fs')
// 使用回调函数的方式实现
fs.readFile("./resource/1.html", (err, data1) => {
if (err) throw err
fs.readFile("./resource/2.html", (err, data2) => {
if (err) throw err
fs.readFile("./resource/3.html", (err, data3) => {
if (err) throw err
console.log(data1 + data2 + data3);
})
})
})
const util = require('util')
// util.promisify可以将API转换成返回值为Promise形式的格式
const mineReadFile = util.promisify(fs.readFile)
// async和await实现
async function main() {
try {
let data1 = await mineReadFile('./resource/1.html')
let data2 = await mineReadFile('./resource/2.html')
let data3 = await mineReadFile('./resource/3.html')
console.log(data1 + data2 + data3);
} catch (e) {
console.log(e);
}
}
main()
- axios请求
async 和 await一般和AJAX
请求结合使用,await后面接Promise对象,而AJAX请求的结果是Promise对象。
我们一般使用axios封装的AJAX(返回值是Promise对象)进行接口的请求。
const reqPolicy = async () => {
const resPolicy = await $axios({
method: 'get',
url: '/api/地址'
})
.then(res => res.data)
.then(data => ({
host: data.host,
key: data.key,
policy: data.policy,
OSSAccessKeyId: data.OSSAccessKeyId,
callback: data.callback,
signature: data.signature,
expire: data.expire
}))
return resPolicy
}
- 直接返回Promise对象
await后面的函数可以直接返回Promise对象来实现异步代码的同步实现:
async function Fun1() {
return new Promise(resolve => setTimeout(resolve('Fun1 执行完毕'), 1000)); // 等待 1 秒
}
async function main() {
console.log('开始执行');
await Fun1(); // 等待 Fun1 执行完毕
console.log('继续执行');
}
main();
输出结果:
开始执行
Fun1 执行完毕
继续执行
- async await嵌套使用
await后面接Promise对象才会实现等待的效果,不要忘记async
的返回结果也是Promise对象,所以如果await后面的函数中也使用了async,await那么也会等待该函数执行完才会执行下面的代码。
eg:
async function Fun1() {
await new Promise(resolve => setTimeout(resolve, 1000)); // 等待 1 秒
console.log('Fun1 执行完毕');
console.log('继续执行在这个后面执行哦')
}
async function main() {
console.log('开始执行');
await Fun1(); // 等待 Fun1 执行完毕
console.log('继续执行');
}
main();
输出结果:
开始执行
Fun1 执行完毕
继续执行在这个后面执行哦
继续执行
使用这种方式比直接返回Promise对象更灵活,因为在await后面还可以直接写代码,这部分代码,在await后面的异步任务执行后才执行。
常用的结构:
async function Fun1() {
await axios({
url: API.deleteConfig,
method: 'post',
data
}).then(res => res.data)
.then(data => {
resolve(data)
}).catch(error => {
reject(error)
})
console.log('请求执行完毕');
}
async function main() {
console.log('开始执行');
await Fun1(); // 等待 Fun1 执行完毕
console.log('继续执行');
}
main();
输出结果:
开始执行
Fun1 执行完毕
继续执行在这个后面执行哦
继续执行