状态
PromiseState
Pending
Resolved(fulfilled)
Rejected
状态变化:Pending->Resolved,Pending->Rejected
PromiseResult
保存着对象成功或失败的结果
resolve
reject
可以修改这个属性的结果
特点
- 状态不受外界影响,只有异步操作的结果才能改变
- 一旦状态改变,就不会在变。
流程
入门样例
<!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>
<button>开始抽奖</button>
<script>
var btn = document.querySelector("button");
function createNumber() {
return Math.floor(Math.random() * 100);
}
btn.addEventListener("click", () => {
var p = new Promise((resolve, reject) => {
var num = createNumber();
setTimeout(() => {
if (num < 30) {
resolve(num);
} else {
reject(num);
}
}, 1000);
});
p.then((value)=>{
alert(`中奖了${
value}`);
}, (reason)=>{
alert(`没mei${
reason}`);
});
})
</script>
</body>
</html>
操作文件
const fs = require("fs");
// fs.readFile('test.txt', (err, data)=>{
// if(err) throw err;
// console.log(data.toString());
// });
const p = new Promise((resole, reject)=>{
fs.readFile('test.txt',(err, data)=>{
if (err) reject(err);
resole(data);
});
})
p.then((value)=>{
console.log(value.toString());
},(reason)=>{
throw 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>
<button>ajax请求</button>
<script>
let btn = document.querySelector("button");
btn.addEventListener("click", function () {
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("get", "https://api.apiopen.top/getAllUrl");
// method, url, async
// 监听xhr.readyState属性值的变化,当这个属性值发生变化时,
// 会执行这个回调函数
xhr.onreadystatechange = function () {
if (xhr.readyState == 4){
// readyState有0,1,2,3,4五种状态,4 === XMLHttpRequest.DONE
// 表示下载操作以完成
if (xhr.status == 200){
resolve(xhr.response);
}else{
reject(xhr.status);
}
}
};
xhr.send();
});
p.then((value)=>{
console.log(value);
},(reason)=>{
console.log(reason);
});
});
</script>
</body>
</html>
封装读取文件内容的函数
/**
* 封装函数读取文件内容
* 参数:文件路径
* 返回值:promise对象
*/
function readFile(path){
return new Promise((resolve, reject)=>{
require('fs').readFile(path, (err,data)=>{
if (err) reject(err);
resolve(data);
});
});
}
readFile('test.txt').then(value=>{
console.log(value.toString());
},reason=>{
console.log(reason);
});
引入node.js的util模块的promisfy方法,封装函数
const util = require('util');
const fs = require('fs');
// promisify参数是个err first类型的函数
let readFile = util.promisify(fs.readFile);
readFile('test.txt').then(value=>{
console.log(value.toString());
},reason=>{
console.log(reason);
});
Promise对象实例的api
- 构造函数:注意Promise构造函数内部的代码是同步执行的
- then():对成功和失败的回调函数,返回一个promise对象,这个Promise对象的状态由回调函数决定,如果回调函数 throw “error”,那么状态是
reject
;如果 return 非Promise对象,那么状态是resolve
;如果 return Promise,那么状态由这个Promise对象决定。注意then()的两个参数,也就是回调函数是异步执行的,必须等同步代码执行后,在执行异步代码 - catch():对失败的回调函数
Promise对象的api
-
Promise.resolve():返回一个成功或失败的Promise对象
参数:非Promise对象的值,返回一个成功的Promise对象
传入一个Promise对象,则返回的Promise对象的状态与传入参数的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> <script> let p1 = Promise.resolve(11); /** * Promise ·········· __proto__: Promise ·········· [[PromiseState]]: "fulfilled" [[PromiseResult]]: 11 */ console.log(p1); let p2 = Promise.resolve(new Promise((resolve, reject)=>{ resolve("hahha"); })) /** * Promise {<fulfilled>: "hahha"} __proto__: Promise [[PromiseState]]: "fulfilled" [[PromiseResult]]: "hahha" */ console.log(p2); let p3 = Promise.resolve(new Promise((resolve,reject)=>{ reject('error'); })); // Promise {<rejected>: "error"} // __proto__: Promise // [[PromiseState]]: "rejected" // [[PromiseResult]]: "error" p3.catch(reason=>{ console.log(reason); // error }); console.log(p3); </script> </body> </html>
-
Promise.reject():返回一个失败的Promise对象
let p4 = Promise.reject(11); /** __proto__: Promise [[PromiseState]]: "rejected" [[PromiseResult]]: 11 */ let p5 = Promise.reject(new Promise((resolve, reject)=>{ reject('ok'); // resolve('ok'); })) /** __proto__: Promise [[PromiseState]]: "rejected" [[PromiseResult]]: Promise */ console.log(p5);
-
Promise.all():参数promise的数组,只有数组的promise状态全部为成功,结果才为成功,PromiseResult的值就是一个数组;如果数组中的promise只要有一个是失败的话,则返回失败的Promise,且PromiseResult的值是参数数组中第一个失败的Promise的PromiseResult
let p6 = new Promise((resolve, reject) => { resolve('ok'); }); let p8 = Promise.resolve("hhaha"); let p7 = Promise.resolve(1233); let pall = Promise.all([p6, p8, p7]); console.log(pall); /* __proto__: Promise [[PromiseState]]: "fulfilled" [[PromiseResult]]: Array(3) 0: "ok" 1: 1233 2: "hhaha" */
let p6 = new Promise((resolve, reject) => { resolve('ok'); }); let p8 = Promise.reject("hhaha"); let p7 = Promise.reject(1233); let pall = Promise.all([p6, p8, p7]); console.log(pall); // __proto__: Promise // [[PromiseState]]: "rejected" // [[PromiseResult]]: "hhaha"
-
Promise.race():参数:promise数组,返回一个新的Promise,状态由参数数组中第一个完成的Promise的状态决定
let p6 = new Promise((resolve, reject) => { resolve('ok'); }); let p8 = Promise.reject("hhaha"); let p7 = Promise.reject(1233); let p_race = Promise.race([p6, p7,p8]); // 根据数组的顺序判断 console.log(p_race); /* __proto__: Promise [[PromiseState]]: "fulfilled" [[PromiseResult]]: "ok" */
改变Promise状态的3种方式
- resolve
- reject
- throw:pedding->reject
当用then对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>
<script>
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("ok");
}, 1000);
});
p1.then(value => {
console.log(value);
return new Promise((resolve, reject) => {
resolve("success");
});
}).then(value => {
console.log(value);
}).then(value => {
console.log(value);
});
// ok
// success
// undefined
</script>
</body>
</html>
Promise的异常穿透
使用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>
<script>
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("ok");
}, 1000);
});
p1.then(value => {
console.log(value);
return new Promise((resolve, reject) => {
resolve("success");
});
}).then(value => {
console.log(value);
}).then(value => {
console.log(111);
throw "失败;了";
}).catch(value=>{
console.log(value);
});
// ok
// success
// 111
// 失败;了
</script>
</body>
</html>
中断Promise链
返回一个Promise状态为pedding即可
<!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) => {
setTimeout(() => {
resolve("ok");
}, 1000);
});
p1.then(value => {
console.log(value);
return new Promise((resolve, reject) => {
resolve("success");
});
}).then(value => {
console.log(11);
return new Promise(()=>{
});
}).then(value => {
console.log(22);
}).then(value => {
console.log(33);
}).catch(value => {
console.log(value);
});
// ok
// 11
</script>
</body>
</html>
自定义Promise,并封装成类
class Promise {
constructor(executor) {
this.PromiseState = "pending";
this.PromiseResult = null;
this.callbacks = [];
const self = this;
function resolve(data) {
if (self.PromiseState !== "pending") return;
self.PromiseState = "resolve";
self.PromiseResult = data;
// 状态改变,执行回调
setTimeout(() => {
self.callbacks.forEach(item => {
item.onresolve(data);
});
});
}
function reject(data) {
if (self.PromiseState !== "pending") return; // 保证状态只能改变一次
self.PromiseState = "reject";
self.PromiseResult = data;
// 状态改变,执行回调
setTimeout(() => {
self.callbacks.forEach(item => {
item.onreject(data);
});
});
}
try {
// 构造函数内的代码要同步执行
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onresolve, onreject) {
const self = this;
// 设置异常穿透
if (typeof onreject !== "function") {
onreject = reason => {
throw reason;
}
}
if (typeof onresolve !== "function") {
onresolve = value => value;
// onresolve = value = { return 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 (error) {
reject(error);
}
}
// 调用回调函数
if (this.PromiseState === "resolve") {
setTimeout(() => {
callback(onresolve);
});
}
if (this.PromiseState === "reject") {
setTimeout(() => {
callback(onreject);
});
}
// 改变状态执行回调
if (this.PromiseState === "pending") {
this.callbacks.push({
onresolve: function () {
callback(onresolve);
},
onreject: function () {
callback(onreject);
}
});
}
});
}
catch (onreject) {
this.then(undefined, onreject);
}
static resolve(value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(v => {
resolve(v);
}, r => {
reject(r);
});
} else {
resolve(value);
}
});
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}
static all(promises) {
return new Promise((resolve, reject) => {
let count = 0;
let arr = [];
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
// 对象状态成功
count++;
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);
})
}
});
}
}
async函数
- 函数的返回值是一个Promise对象,且该Promise对象的状态由async函数的返回值决定,规则与then()一致
await
-
必须放在async函数内,但是async函数不一定需要await
-
await右边是Promise对象,那么返回Promise成功的状态;如果不是Promise对象,则直接返回原来的数;
-
如果await右边的Promise对象状态是reject(reject()或throw)的,那么需要通过try()catch()捕获reason
<!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="自定义.js"></script> --> </head> <body> <script> async function test(){ let p = new Promise((resolve, reject)=>{ // resolve("ok"); // reject("error"); throw "error"; }); // let res = await p; // ok let res1 = await 1233; // 1233 try{ let res = await p; // 不能省略 }catch(e){ console.log(e) // error } // console.log(res); } test(); </script> </body> </html>
async与await配合使用
const util = require('util'); const fs = require('fs'); const readFile = util.promisify(fs.readFile); // 返回一个Promise对象 async function main(){ let data1 = await readFile("1.txt"); let data2 = await readFile("2.txt"); let data3 = await readFile("3.txt"); console.log(data1+data2+data3); } // 看不见回调函数,但是代码却是异步操作的 main();
async与await发送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> <button>发送ajax</button> <script> function sendAjax(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open("get", url); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { resolve(xhr.response); } else { reject(xhr.status); } } }; xhr.send(); }); } let btn = document.querySelector("button"); btn.addEventListener("click", async function(){ let url = "https://api.apiopen.top/getAllUrl"; let res = await sendAjax(url); console.log(res); }); </script> </body> </html>