介绍Promise其他API
Promise.resolve
表示成功状态
Promise.resolve('我是成功状态').then(value=>{
console.log('进入成功状态') },lose=>{console.log('进入失败状态')})
Promise.reject
表示失败状态,使用的频率比成功的少一点
Promise.reject的巧用:可以抛出错误
new Promise((resolve, reject) => {resolve("后盾人");}).then(value => {//这里没有写对应的reject的方法,所以会出错if (value != "成功") {// throw new Error("fail");return Promise.reject("参数错误");}}).catch(error => {console.log(error + "fail");});
Promise.all
当所有输入的 Promise 都被resloved时,返回的 Promise 也将被resloved(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现(resloved)值的数组。
如果输入的任何 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因。
const first=new Promise((resolve,reject)=>{setTimeout(()=>{resolve('第一个异步')},1000)})//需等待一秒const second=new Promise((reslove,reject)=>{reslove('第二个异步')})//立即可以返回Promise.all([first,second]).then(res=>{console.log(res)//等待一秒再执行then})
Promise.all
接收一个包含多个 Promise
的数组(这里是 [first, second]
),并等待所有 Promise
都解决(resolved)。
根据用户批量获取用户资料
function getUsers(names) {let promises = names.map(name => {return ajax(`http://localhost:8888/php/user.php?name=${name}`);});return Promise.all(promises);//这里可以一次获取多个数据,一个数据获取失败就返回失败()rejected}getUsers(["后盾人"]).then(users => {console.log(users);});
我好像大概懂这玩意的意思了,但是我搭的服务器我忘了Nginx 配置文件路径在哪了。。。所以就这样吧
AJAX(Asynchronous JavaScript and XML)是指通过 JavaScript 异步地向服务器请求数据,而无需刷新页面。这使得网页可以在不重新加载整个页面的情况下更新部分内容
ajax(
http://localhost:8888/php/user.php?name=${name}`)` 这行代码实际上是一个 异步 HTTP 请求,通过 GET
方法向服务器发起请求,向 http://localhost:8888/php/user.php
发送一个带有 name
参数的请求。
Promise.allSettled
和all不同,all是只返回全部resolved的,而allSettled是都返回,但是resolved的返回,rejected的也返回,并且作以区分
const p1 = new Promise((resolve, reject) => {reject("rejected");});const p2 = new Promise((resolve, reject) => {resolve("resolved");});Promise.allSettled([p1, p2]).then(results => {console.log(results);});
Promise.race
发送多个Promise,哪个返回的快选哪个
const first = new Promise((resolve, reject) => {setTimeout(() => {resolve("第一个异步,我比较快");}, 200);});const second = new Promise((resolve, reject) => {setTimeout(() => {resolve("第二个异步,我比较慢");}, 1000);});Promise.race([first,second]).then(results => {console.log(results);}).catch(msg => {console.log(msg);});
返回慢的在这里就不管他
Promise队列
队列原理
下一个Promise等上一个Promise完成后开始执行,其实之前接触过,就是返回Promise的时候,先执行这个Promise,再走外层的Promise
let promise = Promise.resolve("后盾人");promise = promise.then(v => {return new Promise(resolve => {setTimeout(() => {console.log(1);resolve();}, 1000);});});promise.then(v => {return new Promise(resolve => {setTimeout(() => {console.log(2);resolve();}, 1000);});});
只要then每次返回的是Promise,就可以形成队列
使用Map实现Promise队列
function queue(num){let promise=Promise.resolve()num.map(v=>{promise=promise.then(_=>{return new Promise(resolve=>{setTimeout(()=>{console.log(v)resolve()},1000)})})})
}
queue([1,2,3,4,5])
将任务p1和任务p2传入Promise 队列
function queue(num){let promise=Promise.resolve()num.map(v=>{promise=promise.then(_=>{return v()})})}function p1() {return new Promise(resolve => {setTimeout(() => {console.log("p1");resolve();}, 1000);});}function p2() {return new Promise(resolve => {setTimeout(() => {console.log("p2");resolve();}, 1000);});}queue([p1, p2]);
定时器模拟执行时间
reduce封装Promise队列
reduce
遍历数组中的每个元素 n,
每次遍历时,返回一个新的 Promise
,并将其链接到前一个 Promise
上每个 Promise
会延迟 1 秒执行,打印当前值 n
,然后解决 Promise
。
function queue(arr){arr.reduce((promise,n)=>{return promise.then(_=>{return new Promise(resolve => {setTimeout(() => {console.log(n);resolve();}, 1000);});});}, Promise.resolve());//设置reduce的初始值为一个已经resolved的promise}queue([1, 2, 3, 4]);
神奇的reduce!
使用队列渲染数据
我没搭建服务器,就这么看吧:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><title>后盾人</title></head><body></body><script>class User {ajax(user) {let url = `http://localhost:8888/php/user.php`;return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest();xhr.open("GET", `${url}?name=${user}`);xhr.send();xhr.onload = function() {if (this.status == 200) {resolve(JSON.parse(this.response));} else if (this.status == 404) {reject(new HttpError("用户不存在"));} else {reject("加载失败");}};xhr.onerror = function() {reject(this);};});}render(users) {users.reduce((promise, user) => {return promise.then(_ => {return this.ajax(user);}).then(user => {return this.view(user);});}, Promise.resolve());}view(user) {return new Promise(resolve => {let h2 = document.createElement("h2");h2.innerHTML = user.name;document.body.appendChild(h2);resolve();});}}new User().render(["后盾人", "向军"]);</script>
</html>
async与await语法糖
async/await
通过同步的方式执行异步任务
可以看到async
关键字声明了一个异步函数。
async function example() {return '我是async'
}
example().then(v=>{console.log(v)})//我是async
这个方法避免了普通Promise的层层回调的结构
await
会暂停异步函数的执行,直到 Promise
完成(resolved 或 rejected),在 await
后面的代码会等待 Promise
完成后再执行。
async function hd() {let name = await new Promise(resolve => {setTimeout(() => {resolve("后盾人");}, 2000);});let site = await new Promise(resolve => {setTimeout(() => {resolve(name + "houdunren.com");}, 2000);});console.log(site)}hd()//后盾人houdunren.com
如果不用这个语法糖,则需要使用之前学的promise的链式调用:
function hd() {new Promise(resolve => {setTimeout(() => {resolve("后盾人");}, 2000);}).then(name => {return new Promise(resolve => {setTimeout(() => {resolve(name + "houdunren.com");}, 2000);});}).then(site => {console.log(site);})}hd()//后盾人houdunren.com
这样需要频繁的返回new Promise
特性 | async/await 写法 | Promise 链式调用写法 |
---|---|---|
代码风格 | 更像同步代码,易于阅读和维护 | 嵌套较多,代码结构复杂 |
错误处理 | 使用 try/catch | 使用 .catch |
适用场景 | 适合多个异步操作按顺序执行 | 适合简单的异步操作 |
可读性 | 高 | 低 |
在 async
函数中,return
的值会自动被包装成一个 Promise
。
那你就要问了:为什么这么写,居然是字符串类型
async function example() {return '我是async';
}example().then(v => {console.log(typeof v); //string
});
因为async函数返回的 Promise
的解决值是 '我是async'
,而不是 Promise
本身;.then
中的回调函数接收的是 Promise
的解决值,而不是 Promise
对象
所以example().then()里,这个then()里接收到的返回值也就是v,是Promise的【解决值】,也就是【我是async】,而不是promise对象本身,【我是async】是个字符串嘛,所以是string
解决值(resolved value)
可以这么验证是不是promise
async function example() {return '我是async';
}const result = example();
console.log(result instanceof Promise); // 输出: true
async/await执行异步请示栗子(没搭建服务器照例摆个代码在这里)
async function get(name) {let host = "http://localhost:8888/php";let user = await ajax(`${host}/user.php?name=${name}`);let lessons = await ajax(`${host}/houdunren.php?id=${user.id}`);console.log(lessons);}get("后盾人");
async写延迟函数
async function sleep(delay=2000) {return new Promise(resolve=>{setTimeout(()=>{resolve()},delay)})
}
async function show() {for(const user of ['我是1','我是2']){await sleep()//不加await会直接打印内容,不会延迟//因为await要等待new Promise返回过来console.log(user)}
}
show()
await写进度条
进度条随着数据的读取加载
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><title>后盾人</title></head><body><style>div {height: 50px;width: 0px;background: #8e44ad;display: flex;justify-content: center;align-items: center;font-size: 30px;color: #fff;}</style><div id="loading">0%</div></body><script src="js/ajax.js"></script><script>function query(name) {return ajax(`http://localhost:8888/php/user.php?name=${name}`);}(async () => {let users = ["后盾人", "李四", "赵六"];for (let i = 0; i < users.length; i++) {let user = await query(users[i]);let progress = ((i + 1) / users.length) * 100;loading.style.width = progress + "%";loading.innerHTML = Math.round(progress) + "%";}})()</script>
</html>
ajax:
class ParamError extends Error {constructor(msg) {super(msg);this.name = "ParamError";}
}
class HttpError extends Error {constructor(msg) {super(msg);this.name = "HttpError";}
}
function ajax(url) {return new Promise((resolve, reject) => {// loading.style.display = "block";if (!/^http/.test(url)) {throw new ParamError("请求地址格式错误");}let xhr = new XMLHttpRequest();xhr.open("GET", url);xhr.send();xhr.onload = function() {if (this.status == 200) {resolve(JSON.parse(this.response));} else if (this.status == 404) {// throw new HttpError("用户不存在");reject(new HttpError("用户不存在"));} else {reject("加载失败");}};xhr.onerror = function() {reject(this);};});
}
php:
<?php
header("Access-Control-Allow-Origin:*");
$users = [['id' => 1, 'name' => '后盾人', 'email' => 'admin@houdunren.com'],['id' => 2, 'name' => '向军', 'email' => '2300071698@qq.com'],['id' => 3, 'name' => '李四', 'email' => 'lisi@qq.com'],['id' => 4, 'name' => '王五', 'email' => 'wangwu@qq.com'],['id' => 5, 'name' => '赵六', 'email' => 'zaoliu@qq.com'],
];
//根据用户名获取用户
if (isset($_GET['name'])) {$response = array_filter($users, function ($user) {return $user['name'] == $_GET['name'];});if ($response) {die(json_encode(array_pop($response)));} else {die(header('HTTP/1.1 404'));}
}
//根据编号获取用户列表
if (isset($_GET['id'])) {$ids = explode(',', $_GET['id']);$response = array_filter($users, function ($user) use ($ids) {return in_array($user['id'], $ids);});die(json_encode($response));
}
类与await的结合
如果一个类里面包含then方法,那么他会包装成promise
class User {constructor(name) {this.name = name;}then(resolve, reject) {let user = ajax(`http://localhost:8888/php/user.php?name=${this.name}`);resolve(user);}}async function get() {let user = await new User("后盾人");console.log(user);}get();
User类里的then方法的存在使得User的实例可以被await使用,因为await会尝试调用对象的then方法,在异步函数get里,await会等待new User()返回的实例,并接受这个返回的数据,这个返回的数据就是ajax请求的数据,也就是user
把数据处理封装在内部
如果直接封装在内部:
class User {get() {let user = ajax(`http://localhost:8888/php/user.php?name=${this.name}`);user.name+='-houdunren.com'console.log(user);return user}}new User().get('荷叶饭');
发现user.name是undefined,为什么?
因为ajax是异步的,下面是user.name='-houdunren.com'是同步的,按照执行顺序来说是先同步再异步,所以先进行了user.name='-houdunren.com',此时ajax还没申请到数据,所以为undefined
对上面的代码做更改
class User {async get(name) {let user = await ajax(`http://localhost:8888/php/user.php?name=${name}`);user.name += "-houdunren.com";return user;}}new User().get("后盾人").then(user => {console.log(user);});
要等待异步先完成哦
async和await的多种声明
标准函数声明
async function fetchData() {const response = await fetch('https://api.example.com/data');const data = await response.json();return data;
}fetchData().then(data => console.log(data));
函数表达式就是将async赋值给一个变量
箭头函数也可以:
const fetchData = async () => {}
在对象中声明 async
方法:
const object = {async fetchData() {}
};api.fetchData().then(data => console.log(data));
在类里声明:
class ApiClient {async fetchData() {}
}const client = new ApiClient();
client.fetchData().then(data => console.log(data));
async基本错误处理
也可以用throw和catch:
async function hd(name) {// throw new Error("fail")//也可以return ajax(`http://localhost:8888/php/user.php?name=${name}`);}hd("后盾人视频").then(user => {console.log(user);}).catch(error => {console.log("Error" + error);});
标准的await错误处理流程
接收到错误的处理,可以使用try、catch,写里面和写外面都可以
async function hd(name) {let user = await ajax(`http://localhost:8888/php/user.php?name=${name}`);let lessons = await ajax(`http://localhost:8888/php/houdunren.php?id=${user.id}`);return lessons;}hd("后盾人教程").then(lessons => {console.log(lessons);}).catch(error => {alert(error.message);});
async function hd(name) {try{let user = await ajax(`http://localhost:8888/php/user.php?name=${name}`);let lessons = await ajax(`http://localhost:8888/php/houdunren.php?id=${user.id}`);return lessons;
}catch(error){alert(error.message)
}}hd("后盾人教程").then(lessons => {console.log(lessons);})
有错误一定要处理
await并行执行技巧
Promise
是异步的,它们的执行不会阻塞主线程,
当调用 p1()
和 p2()
时,两个 Promise
会立即开始执行,并分别在 2 秒后解决。
function p1() {return new Promise(resolve => {setTimeout(() => {resolve("houdunren");}, 2000);});}function p2() {return new Promise(resolve => {setTimeout(() => {resolve("hdcms");}, 2000);});}async function hd() {let res = await Promise.all([p1(), p2()]);//达到并行效果console.log(res);
-
因为
p1
和p2
的setTimeout
都是 2 秒,所以它们会在几乎相同的时间完成。 -
总执行时间约为 2 秒(并行执行),而不是 4 秒(串行执行是4秒)