promise 是什么
- 主要用于异步计算
- 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
- 可以在对象之间传递和操作 promise,帮助我们处理队列
异步加载图片体验异步操作
下面例子使用体验异步加载图片,图片加载成功后返回图片信息,否则返回失败
没有使用 Promise 可以看到代码非常复杂
1 2 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 微任务队列
1 2 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 会创建一个微任务,所以同步任务执行完毕后执行微任务,最后打印成功
。
宏任务和微任务的执行顺序
1 2 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("普通的同步任务");
|
宏任务的提升和误解
1 2 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 中只要状态发送出去之后就不能再改变
1 2 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 的基本语法
1 2 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
1 2 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 返回出来的值
1 2 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 封装
1 2 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 的多种错误处理机制
1 2 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 实现异步加载动画
1 2 3
| #loading { display: none; }
|
1 2 3 4 5
| <button onclick="getcode()">测试</button> <span id="loading"> 加载中... </span> <ul id="ul"></ul>
|
1 2 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 异步加载图片
1 2 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
1 2 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 实现方块移动动画
1 2 3 4 5 6
| #div { width: 100px; height: 100px; background-color: #09c; position: absolute; }
|
1 2 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 中
1 2 3 4 5 6 7 8
| function getname() { let name = "宋十三"; return Promise.resolve(name); }
getname().then((res) => { console.log(res); });
|
promise.reject 语法
此方法会使 promise 的返回值一直在 catch 中
1 2 3 4 5 6
| function errfun() { return Promise.reject("失败"); } errfun().catch((err) => { console.log(err); });
|
promise.all 语法
此方法接收一个方法数组,会同时执行这个数组中的方法,等待数组中的所有方法都执行完毕后返回结果,如果有任何一个方法返回失败则进入 catch 中
1 2 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); });
|
1 2 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"
1 2 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()只会返回请求结果最快的那个函数
1 2 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 中
1 2 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 队列
1 2 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
1 2 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());
|
使用队列渲染数据
1 2 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,也就是内部回调
1 2 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
1 2 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 延迟函数
1 2 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 制作加载进度条
1 2 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%; }
|
1 2
| <div class="loading">50%</div> <button onclick="getuser()">加载</button>
|
1 2 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 的结合
1 2 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();
|
异步封装在类的内部
1 2 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 的多种声明方式
1 2 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 的错误处理
1 2 3 4 5 6 7 8 9 10 11
| async function getname() { console.log(a); console.log("6666"); } getname() .then((res) => {}) .catch((err) => { });
|
标准的 await 函数处理技巧
1 2 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,最后返回的结果一定是方法执行后获取到的结果
1 2 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); });
|