promise 是什么
- 主要用于异步计算
- 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
- 可以在对象之间传递和操作 promise,帮助我们处理队列
异步加载图片体验异步操作
下面例子使用体验异步加载图片,图片加载成功后返回图片信息,否则返回失败
没有使用 Promise 可以看到代码非常复杂
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | function loadimg(src, resolve, reject) {let image = new Image();
 image.src = src;
 image.onload = resolve(image);
 image.onerror = reject;
 }
 loadimg(
 "./image/preview (1).jpg",
 (image) => {
 document.body.appendChild(image);
 },
 () => {
 console.log("失败");
 }
 );
 console.log("加载图片");
 
 | 
promise 微任务队列
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | let age = "18";
 
 
 function getname(name) {
 return new Promise((resolve, reject) => {
 if (name.indexOf("宋") !== -1) {
 age = "success";
 resolve("成功");
 } else {
 age = "error";
 reject("失败");
 }
 });
 }
 getname("宋十三")
 .then((res) => {
 console.log(res);
 })
 .catch((err) => {
 console.log(err);
 });
 console.log(age);
 
 | 
上述例子中先声明了age = "18",代码从上往下执行,又个 age 赋值了 success,由于同步任务优先执行,所以先打印success,Promise 会创建一个微任务,所以同步任务执行完毕后执行微任务,最后打印成功。
宏任务和微任务的执行顺序
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | setTimeout(() => {
 console.log("宏任务");
 }, 0);
 
 new Promise((resolve, reject) => {
 
 resolve();
 console.log("promise的同步任务");
 }).then(() => {
 console.log("微任务");
 });
 console.log("普通的同步任务");
 
 | 
宏任务的提升和误解
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | new Promise((resolve, reject) => {
 
 
 
 setTimeout(() => {
 resolve();
 console.log("同步任务中的宏任务");
 }, 0);
 }).then(() => {
 console.log("宏任务执行完毕后的微任务");
 });
 console.log("同步任务");
 
 | 
promise 单一状态和任务中转
promise 中只要状态发送出去之后就不能再改变
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | let p = new Promise((resolve, reject) => {resolve("成功");
 });
 new Promise((resolve, reject) => {
 setTimeout(() => {
 resolve(p);
 }, 1000);
 
 
 reject("失败");
 })
 .then((res) => {
 console.log(res, "success");
 })
 .catch((err) => {
 console.log(err, "error");
 });
 console.log("同步任务");
 
 | 
promise.then 的基本语法
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | new Promise((resolve, reject) => {reject("失败");
 })
 
 
 .then(null, (err) => {
 console.log("半路拦截处理结果,不在往后继续执行");
 })
 .then(null, (err) => {
 console.log(err);
 });
 
 | 
promise.then 也是一个 promise
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | let p1 = new Promise((resolve, reject) => {resolve("成功");
 });
 let p2 = p1.then(
 (suc) => {
 console.log("p2的成功");
 },
 (err) => {
 console.log("p2的失败");
 }
 );
 setTimeout(() => {
 console.log(p1);
 console.log(p2);
 });
 
 | 
then 返回值的处理技巧
then 里面的返回值会交给下一个 then 去处理,并且下一个 then 拿到的是上一个 then 返回出来的值
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 
 | let p1 = new Promise((resolve, reject) => {resolve("p1失败");
 })
 .then(
 (suc) => {
 return new Promise((resolve, reject) => {
 resolve("p1解决交给p2");
 });
 },
 (err) => {
 console.log(err);
 }
 )
 .then(
 (res) => {
 return "p2交个p3";
 },
 (err) => {
 console.log(err);
 }
 )
 
 .then((res) => {
 console.log(res);
 });
 
 | 
其他类型的 promise 封装
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 
 | new Promise((resolve, reject) => {resolve();
 })
 .then((res) => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 class hd {
 then(resolve, reject) {
 setTimeout(() => {
 resolve("成功");
 }, 1000);
 }
 }
 return new hd();
 })
 .then((res) => {
 console.log(res);
 });
 
 | 
promise 的多种错误处理机制
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 
 | function getname() {return new Promise((resolve, reject) => {
 reject("name失败");
 });
 }
 function getage() {
 return new Promise((resolve, reject) => {
 resolve("age成功");
 });
 }
 getname()
 .then((res) => {
 return getage();
 })
 .then((res) => {
 console.log(res);
 })
 .catch((err) => {
 
 
 console.log(err);
 });
 
 | 
使用 finally 实现异步加载动画
| 12
 3
 
 | #loading {display: none;
 }
 
 | 
| 12
 3
 4
 5
 
 | <button onclick="getcode()">测试</button><span id="loading">
 加载中...
 </span>
 <ul id="ul"></ul>
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 
 | function getcode() {let loading = document.querySelector("#loading");
 let ul = document.querySelector("#ul");
 ul.innerHTML = "";
 
 loading.style.display = "block";
 ajax("http://localhost:3000/search/hot/detail")
 .then((res) => {
 console.log(res);
 let data = res.data;
 data.forEach((item) => {
 
 let li_node = document.createElement("li");
 
 let text = document.createTextNode(item.content);
 
 li_node.appendChild(text);
 
 ul.appendChild(li_node);
 });
 })
 .finally(() => {
 
 loading.style.display = "none";
 });
 }
 
 | 
Promise 异步加载图片
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | function onloadImg(src) {return new Promise((resolve, reject) => {
 let img = new Image();
 img.src = src;
 img.onload = () => {
 resolve(img);
 };
 img.onerror = () => {
 reject(img);
 };
 });
 }
 onloadImg("https://songzhengxiang.gitee.io/blog/assets/img/logo.png")
 .then((res) => {
 console.log(res);
 res.style.border = "2px solid red";
 document.body.appendChild(res);
 })
 .catch((err) => {
 console.log(err);
 });
 
 | 
封装 setTimeout
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | function timeout(time = 1000) {
 return new Promise((resolve) => setTimeout(resolve, time));
 }
 timeout()
 .then(() => {
 console.log("第一次");
 
 return timeout(2000);
 })
 .then(() => {
 console.log("第二次");
 });
 
 | 
扁平化构建 setinterval
使用 promise 封装 setinterval 实现方块移动动画
| 12
 3
 4
 5
 6
 
 | #div {width: 100px;
 height: 100px;
 background-color: #09c;
 position: absolute;
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 
 | function interval(time = 1000, calback) {
 
 return new Promise((resolve) => {
 
 let id = setInterval(() => {
 
 
 
 calback(id, resolve);
 }, time);
 });
 }
 
 interval(7, (id, resolve) => {
 
 let div = document.querySelector("#div");
 
 let left = parseInt(window.getComputedStyle(div).left);
 
 div.style.left = left + 1 + "px";
 if (left > 500) {
 clearInterval(id);
 resolve(div);
 }
 }).then((div) => {
 interval(10, (id, resolve) => {
 let width = parseInt(window.getComputedStyle(div).width);
 div.style.width = width - 1 + "px";
 if (width <= 10) {
 div.style.background = "red";
 clearInterval(id);
 }
 });
 });
 
 | 
promise.resolve 语法
此方法会使 promise 的返回值一直在 then 中
| 12
 3
 4
 5
 6
 7
 8
 
 | function getname() {let name = "宋十三";
 return Promise.resolve(name);
 }
 
 getname().then((res) => {
 console.log(res);
 });
 
 | 
promise.reject 语法
此方法会使 promise 的返回值一直在 catch 中
| 12
 3
 4
 5
 6
 
 | function errfun() {return Promise.reject("失败");
 }
 errfun().catch((err) => {
 console.log(err);
 });
 
 | 
promise.all 语法
此方法接收一个方法数组,会同时执行这个数组中的方法,等待数组中的所有方法都执行完毕后返回结果,如果有任何一个方法返回失败则进入 catch 中
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | let promise1 = new Promise((resolve, reject) => {reject("失败1");
 });
 let promise2 = new Promise((resolve, reject) => {
 resolve("成功2");
 });
 
 Promise.all([promise1, promise2])
 .then((res) => {
 console.log(res);
 })
 .catch((err) => {
 console.log(err);
 });
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | function getMusics(music) {
 let promises = music.map((item) => {
 return ajax(`http://localhost:3000/search?keywords=${item}`);
 });
 
 return Promise.all(promises);
 }
 getMusics(["少年", "桥边姑娘"]).then((res) => {
 console.log(res);
 });
 
 | 
promise.allSettled 语法
promise.allSettled 不在乎方法数组中的方法是否都是成功,如果有失败的 promise 则会返回
status = "rejected"
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 
 | let pro1 = new Promise((resolve, reject) => {let data = [
 {
 name: "xxx",
 code: "111xxx",
 },
 ];
 
 reject(data);
 });
 let pro2 = new Promise((resolve, reject) => {
 resolve("成功1");
 });
 
 
 Promise.allSettled([pro1, pro2]).then((res) => {
 
 res = res.filter((item) => {
 return item.status === "fulfilled";
 });
 console.log(res);
 });
 
 | 
promise.race 语法
Promise.race 同样接受一个方法数组,但是 Promise.race()只会返回请求结果最快的那个函数
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | let promises = [new Promise((resolve, reject) => {
 setTimeout(() => {
 resolve("成功1");
 }, 1000);
 }),
 new Promise((resolve, reject) => {
 setTimeout(() => {
 resolve("成功2");
 }, 500);
 }),
 ];
 
 Promise.race(promises).then((res) => {
 console.log(res);
 });
 
 | 
promise 队列原理
当存在连续的.then 时,前面的 then 必须有返回值才会进入到后面的.then 中
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 
 | let prms = Promise.resolve(1);prms = prms.then((res) => {
 console.log(res);
 
 return new Promise((resolve, reject) => {
 setTimeout(() => {
 resolve(2);
 }, 1000);
 });
 });
 prms = prms.then((res) => {
 console.log(res);
 
 return new Promise((resolve, reject) => {
 setTimeout(() => {
 resolve(3);
 }, 1000);
 });
 });
 prms = prms.then((res) => {
 console.log(res);
 
 return new Promise((resolve, reject) => {
 setTimeout(() => {
 resolve(4);
 }, 1000);
 });
 });
 prms = prms.then((res) => {
 console.log(res);
 });
 
 | 
使用 map 实现 promise 队列
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | let nums = [1, 2, 3, 4, 5];function prms(nums) {
 let promises = Promise.resolve();
 nums.map((v) => {
 
 promises = promises.then((_) => {
 return new Promise((resolve) => {
 setTimeout(() => {
 console.log(v);
 resolve();
 }, 1000);
 });
 });
 });
 }
 prms(nums);
 
 | 
reduce 封装 promise
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | let nums = [1, 2, 3, 4];nums.reduce((prms, n) => {
 return prms.then((_) => {
 return new Promise((resolve) => {
 setTimeout(() => {
 console.log(n);
 resolve();
 }, 1000);
 });
 });
 }, Promise.resolve());
 
 | 
使用队列渲染数据
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 
 | let nums = ["张三", "李四", "王五"];class getUser {
 
 ajax(user) {
 return new Promise((resolve) => {
 setTimeout(() => {
 resolve(`这是${user}`);
 }, 1000);
 });
 }
 
 render(users) {
 users.reduce((prms, u) => {
 return prms
 .then(() => {
 return this.ajax(u);
 })
 .then((res) => {
 
 this.view(res);
 });
 }, Promise.resolve());
 }
 view(data) {
 console.log(data);
 let h3 = document.createElement("h3");
 h3.innerHTML = data;
 document.body.appendChild(h3);
 }
 }
 new getUser().render(nums);
 
 | 
async 和 await 语法糖
- async 和 await 是 promise 的语法糖,其内部原理是一样的
- 但是 async 和 await 的写法要比 promise 简单
- async 相当于 new Promise
- await 相当于 then,也就是内部回调
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 
 | function user1() {return new Promise((resolve) => {
 setTimeout(() => {
 console.log("1");
 resolve("张三");
 }, 1000);
 });
 }
 function user2() {
 return new Promise((resolve) => {
 setTimeout(() => {
 console.log("2");
 resolve("李四");
 }, 1000);
 });
 }
 
 
 async function getname() {
 
 let name1 = await user1();
 let name2 = await user2();
 
 return [name1, name2];
 }
 getname().then((res) => {
 console.log(res);
 });
 
 | 
async 和 await 执行异步操作
加上 await 后必须等待前一个 await 返回状态后再继续执行后面的 await
| 12
 3
 4
 5
 6
 
 | async function getuser() {let list1 = await ajax("http://localhost:3000/search/hot/detail");
 let list2 = await ajax("http://localhost:3000/search/hot/detail");
 console.log([list1, list2]);
 }
 getuser();
 
 | 
await 延迟函数
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | function sleep(time = 2000) {return new Promise((resolve) => {
 setTimeout(() => {
 resolve();
 }, time);
 });
 }
 async function getuser() {
 for (const user of ["张三", "李四"]) {
 await sleep();
 console.log(user);
 }
 }
 getuser();
 
 | 
await 制作加载进度条
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | .loading {height: 20px;
 line-height: 20px;
 text-align: center;
 background-color: rebeccapurple;
 color: white;
 transition: 0.5s;
 width: 0%;
 }
 
 | 
| 12
 
 | <div class="loading">50%</div><button onclick="getuser()">加载</button>
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | function shell(time = 300) {return new Promise((resolve) => {
 setTimeout(() => {
 resolve();
 }, time);
 });
 }
 async function getuser() {
 let loading = document.querySelector(".loading");
 loading.style.width = "0%";
 for (let i = 0; i < 10; i++) {
 await shell();
 let progress = ((i + 1) / 10) * 100;
 loading.style.width = progress + "%";
 loading.innerHTML = Math.round(progress) + "%";
 console.log(i);
 }
 }
 
 | 
class 和 await 的结合
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | class getuser {constructor(name) {
 this.name = name;
 }
 then(resolve, reject) {
 resolve(this.name);
 }
 }
 async function getname() {
 let user = await new getuser("李四");
 console.log(user);
 }
 getname();
 
 | 
异步封装在类的内部
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 
 | function ajax(name) {return new Promise((resolve) => {
 resolve(name);
 });
 }
 class getuser {
 async get(name) {
 let user = await ajax(name);
 return user + ",你好";
 }
 
 
 
 
 
 
 
 
 }
 new getuser().get("张三").then((res) => {
 console.log(res);
 });
 
 | 
async 和 await 的多种声明方式
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 
 | function ajax() {return new Promise((resolve) => {
 resolve("hello,world");
 });
 }
 
 async function getname() {
 return await ajax();
 }
 getname().then((res) => {
 console.log(res);
 });
 
 
 let user = async function getname() {
 return await ajax();
 };
 user().then((res) => {
 console.log(res);
 });
 
 
 let name = {
 async getname() {
 return await ajax();
 },
 };
 name.getname().then((res) => {
 console.log(res);
 });
 
 
 class userinfo {
 async getname() {
 return await ajax();
 }
 }
 new userinfo().getname().then((res) => {
 console.log(res);
 });
 
 | 
async 的错误处理
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | async function getname() {
 console.log(a);
 console.log("6666");
 }
 getname()
 .then((res) => {})
 .catch((err) => {
 
 
 });
 
 | 
标准的 await 函数处理技巧
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 
 | function ajax(name) {return new Promise((resolve, reject) => {
 resolve(name + "你好");
 });
 }
 async function getname() {
 
 
 
 try {
 let name = await errfun("张三");
 let age = `今年${name}芳龄18`;
 return age;
 } catch (error) {
 
 console.log(error);
 }
 }
 getname()
 .then((res) => {
 console.log(res);
 })
 .catch((err) => {
 
 console.log("sdfsdf");
 console.log(err);
 });
 
 | 
await 并行执行方法
并行执行用来处理多个方法之间不互相关联时,但是又想等待所有方法后获取到正确的结果
这时可以使用 promis.all 来处理,前面加上 await,最后返回的结果一定是方法执行后获取到的结果
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | function f1() {return Promise.resolve("p1");
 }
 function f2() {
 return Promise.resolve("p2");
 }
 async function getname() {
 
 
 let users = await Promise.all([f1(), f2()]);
 
 return users;
 }
 getname().then((res) => {
 console.log(res);
 });
 
 |