超级重要 面试必备
可恶 其实主要就是想学那个async和await 发现自己什么都不会
🍔正文开始啦 !!!
Promise的理解和使用
1、概念:
通俗来说 Promise是一个承诺 承诺的事情不一定可以完成 但是会返回一个结果
- pending 正在做。。。
- Resolve 完成这个承诺
- Reject 失败了
2、特点:
(1)只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。
3、缺点:
(1)无法取消Promise,一旦新建它就会立即执行,无法中途取消。和一般的对象不一样,无需调用。
(2)如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
(3)当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
什么是Promise ?
其实就是JS中对于 进行异步编程的新解决方案
之前的解决方案是 单纯的使用回调函数
关于异步操作
首先先了解一下并行 并发 同步和 异步
1. 并行
就是指计算机可以同时执行多个任务 进程或者上下文切换
2. 同步和异步
同步: 任务完成才可以执行下一个任务
异步: 各个任务之前不会影响 也就是多线程编程
JS中的异步操作 ~
js没有多线程概念 但是通过函数回调机制依然可以做到单线程的 并发
比如
js中的fetch,fetch可以同时访问多个资源调用
fetch的时候 程序不会等待 而是会继续执行下去 获取到网络资源的时候才会调用回调函数 主程序和回调函数其实还是在同一个线程上的这就是
JS实现单线程的并发什么操作时和异步编程呢?
进行网络编程的时候经常会执行网络请求 数据库访问和文件系统读取 这时候就很适合使用异步编程 多线程的话会浪费I/O资源
异步编程有什么?
为什么要用promise ?
1.指定回调函数的方式更加灵活
2.支持连续调用 避免回调地狱👇
promise
链式调用,用来解决回调地狱问题,但是
只是简单的改变格式,并没有彻底解决上面的问题真正要解决上述问题,一定要利用promise再加上await和async关键字实现异步传同步
终极解决方法
promise + async / await
promise初体验
使用promise封装基于定时器的异步
> Date.now() //获取当前的一个时间戳
<script >
function doDelay(time) {
// 1. 创建 promise 对象(pending 状态), 指定执行器函数
return new Promise((resolve, reject) => {
// 2. 在执行器函数中启动异步任务
console.log('启动异步任务')
setTimeout(() => {
console.log('延迟任务开始执行...')
const time = Date.now() // 假设: 时间为奇数代表成功, 为偶数代表失败
if (time % 2 === 1) { // 成功了
// 3. 1. 如果成功了, 调用 resolve()并传入成功的 value
resolve('成功的数据 ' + time)
} else { // 失败了
// 3.2. 如果失败了, 调用 reject()并传入失败的 reason
reject('失败的数据 ' + time)
}
}, time)
})
}
const promise = doDelay(2000)
promise.then(// promise 指定成功或失败的回调函数来获取成功的 vlaue 或失败的 reason
value => {// 成功的回调函数 onResolved, 得到成功的 vlaue
console.log('成功的 value: ', value)
},
reason => { // 失败的回调函数 onRejected, 得到失败的 reason
console.log('失败的 reason: ', reason)
},
)
</script>
2. 使用promise封装AJAX请求
<script >
/*
可复用的发 ajax 请求的函数: xhr + promise
*/
function promiseAjax(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return
const {
status,
response
} = xhr
// 请求成功, 调用 resolve(value)
if (status >= 200 && status < 300) {
resolve(JSON.parse(response))
} else { // 请求失败, 调用 reject(reason)
reject(new Error('请求失败: status: ' + status))
}
}
xhr.open("GET", url)
xhr.send()
})
}
promiseAjax('https://api.apiopen.top2/getJoke?page=1&count=2&type=vid
eo ')
.then(
data => {
console.log('显示成功数据', data)
},
error => {
alert(error.message)
}
) </script>
Promise实操 -- fs模块
回调函数形式
fs.readFile( () => {
})
Promise形式
let p = new Promise ((resolve, reject) => {
fs.readFile('', (err, data) => {
if(err) reject(err);
resolve(data);
})
});
p.then((value) => {
console.log(value.toString());
}, (reason) => {
console.log(reason);
})
Promise实操 -- AJAX模块
<template>
<button @click = "add">Promise 封装 AJAX</button>
</template>
<script>
import { ref, reactive } from 'vue'
export default {
setup(){
const add = new Promise((resolve, reject) => {
const xhr = new xmlHttpRequest();
xhr.open('GET', 'http://127.0.0.1:8000/serve')
xhr.send();
xhr.onreadystatechange() {
if(xhr.onreadystate == 4){
if(xhr.state >=200 && xhr.state < 300) {
resolve(xhr.response);
}else{
reject(xhr.status);
}
}
}
});
add.then(value => {
consolo.log(value);
}, reason => {
console.log(reason);
});
return { add };
}
}
</script>
Promise封装 -- fs模块
util.promisify(original)
传入一个遵循常见错误优先的回调风格的函数 以 (err, value) => ...回调作为最后一个参数 并返回一个peomise的版本
其实就是可以直接变成promise的封装方式 不用手动去封装
Promise封装联系 -- AJAX模块
封装一个函数
sendAJAX发送GET AJAX请求参数 URL
返回结果 Promise对象
const sendAJAX(url) {
return new Promise((resolve, reject) => {
const xhr = new XmlHttpRequset();
xhr.responseType = 'json';
xhr.open('GET', url);
xhr.send();
xhr.onreadystatechange = function{
if(xhr.readystate === 4) {
if(xhr.state >= 200 && xhr.state < 300) {
resolve(xhr.response);
}else{
reject(xhr.status);
}
}
}
};
sendAJAX('http://127.0.0.1/serve')
.then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
Promise的状态改变
关于Promise的状态
实例对象中的一个属性 【
PromiseState】
pedding未决定的resolved/ fullfilled 成功rejected失败对象的值
实例对象中的一个属性 【
promiseResult】保存着异步任务成功或者失败的结果
resolve
reject
Promise的基本流程
如何使用Promise
Ⅰ- Promise 构造函数: Promise (excutor) {}
(1)
executor函数: 执行器 (resolve, reject) => {}(2)
resolve函数: 内部定义成功时我们调用的函数 value => {}(3)
reject函数: 内部定义失败时我们调用的函数 reason => {}说明: executor 会在 Promise 内部立即
同步调用,异步操作在执行器中执行,换话说Promise支持同步也支持异步操作
Ⅱ-Promise.prototype.then 方法: (onResolved, onRejected) => {}
(1)
onResolved函数: 成功的回调函数 (value) => {}(2)
onRejected函数: 失败的回调函数 (reason) => {}说明: 指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调 返回一个新的 promise 对象
Ⅲ-Promise.prototype.catch 方法: (onRejected) => {}
(1) onRejected 函数: 失败的回调函数 (reason) => {}
说明: then()的语法糖, 相当于: then(undefined, onRejected)
(2) 异常穿透使用:当运行到最后,没被处理的所有异常错误都会进入这个方法的回调函数中
Ⅳ-Promise.resolve 方法: (value) => {}
(1) value: 成功的数据或 promise 对象
说明: 返回一个成功/失败的 promise 对象,直接改变promise状态
let p3 = Promise.reject(new Promise((resolve, reject) => { resolve('OK'); }));
console.log(p3);
Ⅴ-Promise.reject 方法: (reason) => {}
(1) reason: 失败的原因
说明: 返回一个失败的 promise 对象,直接改变promise状态,
代码示例同上
Ⅵ-Promise.all 方法: (promises) => {}
promises: 包含 n 个 promise 的数组
说明: 返回一个新的 promise, 只有所有的 promise
都成功才成功, 只要有一 个失败了就直接失败
let p1 = new Promise((resolve, reject) => { resolve('成功'); })
let p2 = Promise.reject('错误错误错误');
let p3 = Promise.resolve('也是成功')
const result = Promise.all([p1, p2, p3]);
console.log(result);
Ⅶ-Promise.race 方法: (promises) => {}
(1) promises: 包含 n 个 promise 的数组
说明: 返回一个新的 promise,
第一个完成的 promise 的结果状态就是最终的结果状态,如p1延时,开启了异步,内部正常是同步进行,所以
p2>p3>p1,结果是P2
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('Oh Yeah');
//调用
const result = Promise.race([p1, p2, p3]);
console.log(result);
Promise的几个关键问题
如何改变promise对象的状态
-
resolve函数 -
可以把
pending=>fulfilled(resolve) -
reject函数- 可以把
pending=>rejected
- 可以把
-
抛出错误
-
throw('出错啦'); //pending => rejected
-
promise对象指定多个回调
promise对象指定多个回调 成功的化 就都会执行
状态没有改变的话 就还是不会执行
let p = new Promise(())
改变promise状态和指定函数的回调谁先谁后
let p = new Promise((resolve, reject) => {
resolve('OK');
})
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000)
})
p.then(value => {
resolve(value);
}, reason => {
reject(reason);
})
执行同步任务的时候 直接调用resolve 这就是先执行resolve
异步任务的时候 等待一段时间 类似定时器 这个时候就是限制性then方法中
一般都是异步任务居多
先指定回调 再去改变状态 回调的执行时机是在调用resolve之后
Promise.then返回的
Promise如何串联多个操作任务
let p = new Promise((resolve, reject) => {
resolve("OK");
});
p.then( value => {
resolve("success");
}).then(value => {
resolve(value); //success
}).then(value => {
resolve(value); //undefined
})
then方法返回的是上一个函数的返回值 而倒数第二个回调的时候返回的已经是一个成功的promise对象了 而且函数里面没有写return的值 所以最后一个输出的就是undefined
异常穿透
let p = new Promise((resolve, reject) => {
reject("Rrr");
});
p.then( value => {
resolve("success");
}).then(value => {
resolve(value); //success
}).then(value => {
resolve(value); //undefined
}).catch(reason => {
console.warn(reason);
})
使用catch的话就只要传一个参数 但是使用then的话就要传递两个参数
失败的话是由最后一个回调来进行处理的
什么是
pending的promise对象 ?return new Promise(() => {})
如何中断promise链
let p = new Promise((resolve, reject) => {
reject("Rrr");
});
p.then( value => {
console.log(111);
return new Promise(() => {})
}).then(value => {
resolve(value); //success
}).then(value => {
resolve(value); //undefined
}).catch(reason => {
console.warn(reason);
})
状态没有改变就不能执行后面的回调函数 所以这个时候就只会输出 111
手写Promise
基本架构搭建
- index.html
<head> <script src = './promise.js'> </script> ...... </head> let p = new Promise((resolve, reject) => { resolve("OKK"); }); p.then(value=>{ console.log(value); }, reason => { console.warn(reason); })
- promise.js
function Promise(executor) { //resolve 函数 function resolve(data) { } //reject函数 function reject(data) { } //同步调用【执行器函数】 executor(resolve, reject) } //添加then方法 Promise.prototype.then = function(onResolved, onRejected){ }
resolve和reject的结构搭建
- index.html
...... let p = new Promise((resolve, reject) => { resolve("OKK"); reject("Errrrr~"); });
- promise.js
function Promise(executor) { //添加属性 this.PromiseState = 'pending'; this.PromiseResult = null; //保存实力对象 this 的值 const self = this; //self _this that //resolve 函数 function resolve(data) { self.PromiseState = 'resolved'; // fulfilled self.PromiseResult = data; } //reject函数 function reject(data) { self.PromiseState = 'rejected'; // self.PromiseResult = data; } //同步调用【执行器函数】 executor(resolve, reject) } //添加then方法 Promise.prototype.then = function(onResolved, onRejected){ }
抛出异常
- index.html
...... let p = new Promise((resolve, reject) => { throw "error"; });
- promise.js
function Promise(executor) { //添加属性 this.PromiseState = 'pending'; this.PromiseResult = null; //保存实力对象 this 的值 const self = this; //self _this that //resolve 函数 function resolve(data) { self.PromiseState = 'resolved'; // fulfilled self.PromiseResult = data; } //reject函数 function reject(data) { self.PromiseState = 'rejected'; // self.PromiseResult = data; } try{ //同步调用【执行器函数】 executor(resolve, reject) }catch(e) { //修改Primose对象状态为 失败 reject(e); } } //添加then方法 Promise.prototype.then = function(onResolved, onRejected){ }
关于同时写了
resolve和reject就象是这样👇
这样的话就会返回
reject函数的回调 即改变状态两次解决方案:
function resolve(data) { if( self.promiseSta != 'pending' ){ self.PromiseState = 'resolved'; // fulfilled self.PromiseResult = data; } } //reject函数 function reject(data) { if( self.promiseSta != 'pending' ){ self.PromiseState = 'rejected'; // self.PromiseResult = data; } }加上一个判断方法的话 就会只执行一次改变状态的方法了
关于 then 方法
如何在
then方法中获取到promiseState直接在
then方法中使用this.PromiseState就好了 因为这个其实就是调用了前面那个实例化对象p里面的那个thisPromise.prototype.then=function(onResolved,onRejected){ //调用回调函数 PromiseState if(this.PromiseState == 'fulfilleded') { onResolved(this.PromiseResult); } if(this.PromiseState == 'rejected') { onRejected(this.PromiseResult); } }
异步任务
index.js
const p = new Promise((resolve, reject) => {
setTimeout(() => {
reject("error");
}, 1000)
})
因为这里有一个定时器的话 状态就不会立即改变 也就不能及时的调用then方法
解决方案
function Promise(excutor){
......
const callback = {};
function resolve(data) {
....
if(self.callback.onResolved) {
self.callback.onResolved(data);
}
}
function reject(data) {
....
if(self.callback.onRejected) {
self.callback.onRejected(data);
}
}
}
Promise.prototype.then=function(onResolved,onRejected){
.......
//增加一个状态判断 如果是pending的话就调用
if(this.PromiseState == 'pending') {
//保存回调函数
this.callback = {
onResolved, //这个是简写形式 其实就是
onRejected //onResolved = onResolved
}
}
}
指定多个回调
- 例👇
let p = new Promise((resolve, reject) => {
resolve("OKK");
});
p.then(value=>{
console.log(value);
}, reason => {
console.warn(reason);
});
p.then(value=>{
alert(value);
}, reason => {
alert(reason);
})
- 之前方法的问题
这样子保存回调的话 在指定多个回调的时候 后面的回调就会把前面的回调覆盖掉 ~
- 如果用push保存的话👇
callbacks就会以数组的形式保存两个回调 注:回调是以对象的形式被保存起来的
- 所以这样的话在使用成功或者失败的时候就要有一个数组遍历
function Promise(excutor){
......
const callback = {};
function resolve(data) {
....
self.callbacks.forEach(item => {
item.onResolved(data);
});
}
function reject(data) {
....
self.callbacks.forEach(item => {
item.onRejected(data);
})
}
}
Promise.prototype.then=function(onResolved,onRejected){
.......
//增加一个状态判断 如果是pending的话就调用
if(this.PromiseState == 'pending') {
//保存回调函数
this.callback.push = {
onResolved, //这个是简写形式 其实就是
onRejected //onResolved = onResolved
}
}
}
同步任务 then 返回结果
- then方法的返回结果是由他执行的回调函数的结果决定的
- 如果then方法执行的回调中返回的是非promise对象 如数字字符串,那返回也是成功
-
Tip
-
实现👇
const p = new Promise((resolve, reject) => {
resolve('okk');
})
const res = p.then(value => {
//console.log(521) //这个返回结果是undefined
//return "hello"; //这个返回的结果值就是hello
return new Promise((resolve,reject) =>{
resolve("success"); //这个就会调用成功的方法
reject("error"); //这个就会调用失败的方法
throw "FALL"; //这个也会调用失败的方法
})
}, reason => {
console.warn(reason;)
})
Promise.prototype.then=function(onResolved,onRejected){
return new Promise((resolve,reject)=> {
//调用回调函数
if(this.PromiseState == 'fulfilled') {
try {
//获得回调函数的执行结果
let result = onResolve(this.PromiseResult);
//判断
if(result instanceof Promise){
result.then(value=>{
resolve(value);
}, reason=>{
reject(reason);
})
}else{
//结果的对象状态就是成功
resolve(result);
}
}catch{
reject(e);
}
}
})
}
异步任务then返回结果
Promise.prototype.then=function(onResolved,onRejected){
const self = this;
.......
//增加一个状态判断 如果是pending的话就调用
if(this.PromiseState == 'pending') {
//保存回调函数
this.callback.push = {
onResolved: function(){
//执行成功的回调
let result= onResolved(self.PromiseResult);
//判断
if(result instanceof Promise){
result.then(value=>{
resolve(value);
}, reason=>{
reject(reason);
})
}else{
//结果的对象状态就是成功
resolve(result);
}
}
onRejected //onResolved = onResolved
}
}
}
不太懂的顺序
- 首先判断实例化对象的状态 如果是失败则执行 此时的状态为pending
然后就会执行 then 方法中的onRejectd
- 由于有一个定时器 所以现在的状态是pending 所以执行这个👇
\
- 由于
onRejected中没有改变实例化对象的状态 也就是使用
reject() 或者 resolve()
所以实例化对象的状态就还是 pending
下面为解决方案👇
异步修改then结果返回
完美版的 then
\
Catch 方法与异常穿透
\
Promise.resolve 封装
const p = Promise.resolve('OK');
const p2=Promise.resolve(new Promise((resolve,reject)=>{
resolve('success');
}))
Promise.reject 封装
const p = Promise.reject('OK');
const p2=Promise.reject(new Promise((resolve,reject)=>{
resolve('success');
}))
rmise.all封装
let p1 = new Promise((resolve,reject) => {
resolve("sucess");
})
let p2 = Promise.resolve('okkk');
let p3 = Promise.resolve('Oh no');
let result = Promise.all([p1, p2, p3]);
promise.all = function(promises) {
return new Promise((resolve, reject) => {
let count = 0;
let add = [];
for(let i = 0; i < promises.lenght;i++) {
promises[i].then(value => {
count++;
arr[i] = value;
if(count == promises.lenght){
resolve(arr);
}
}, reason=> {
reject(reason);
})
}
})
}
Promise.race 封装
回调函数的异步执行
then指定的回调函数是异步执行的 需要等到同步代码执行完毕之后再执行
所以下面函数👇的输出结果是 111 333 222
let p1 = new Promise((resolve, reject) => {
resolve("seccess");
console.log(111);
});
p1.then(value=> {
console.log(222);
});
console.log(333)
所以在之前的代码中需要改进的地方是👇
//------------第一个地方
Promise.prototype.then=function(onResolved,onRejected){
const self = this;
.......
return new Promise((resolve, reject) => {
//封装函数
function callback(type){}
//调用回调函数 PromiseState
if(self.PromiseState == 'fulfilled'){
//也就是在这里改为异步的
setTimeout(() => {
callback(onResolved);
});
}
if(self.PromiseState == 'rejected'){
//也就是在这里改为异步的
setTimeout(() => {
callback(onRejected);
});
}
})
}
第二个地方
就是那个 function Promise(executor) 那里
let p1 = new Promise((resolve,reject) => {
resolve("sucess");
})
let p2 = Promise.resolve('okkk');
let p3 = Promise.resolve('Oh no');
let result = Promise.race([p1, p2, p3]);
Class版本封装
class Promise {
//构造方法
constructor(executor){}
// then方法封装
then(onResolved, onRejected){};
//catch
catch(onRejected){};
//resolve
static resolve(value){};
//reject
static reject(value){};
//all
static all(value){};
//race
static race(value){};
}
把 function Promise(executor) 函数里的东西放在这里
resolve reject all race方法前要加一个static是因为要表明他是一个类而不是一个实例化对象
async与await
- 只能说为了一个东西学了全部 我就是一个活生生的例子 为了学到这个
async和await硬生生看了全部的这个 不过所幸要看完了 不过后面还是要好多东西md 算了 学!!!!!!
async函数
返回的是一个 promise 对象
promise对象的返回值是由async的返回值决定的
async function main() {
//如果返回值不是一个promise对象的数据
return 521;
//如果是一个promise的对象
return new Promise((resolve, reject) => {
resolve("nice");
reject("oh no");
})
throw 'oh my god~'
}
let result = main();
console.log(result);
await表达式
await右侧的表达式一般是一个promise对象 但是也可以是其他的值- 如果表达式是
promise对象的话await返回的就是promise成功的值 - 如果表达式是其他的值 直接将此值作为
await的返回值就好了 await必须写在async函数中, 但是async函数中可以没有await- 如果await的promise失败了 就会抛出异常 就需要try...catch捕获处理
async function main() {
let p = Promise((resolve,reject)=> {
resolve("okk");
reject("error");
})
//右侧为promise的情况
let res = await p;
右//侧为其他数据类型的数据
let res2 = await 20;
//如果是promise是失败的状态
try{
let res3 = await p;
}catch {
console.log(e);
}
}
async和await的结合
小案例 获取三个文件中的内容 再做内容的拼接
- 老版本
- ⭐新版本
async function main() {
try{
let data1 = await mineReadFile('./1x.html');
let data2 = await mineReadFile('./2x.html');
let data3 = await mineReadFile('./3x.html');
console.log(data1 + data2 + data3);
}catch(e) {
console.log(e.code);
}
}
main();
async 和 await结合发送AJAX
<template>
<button @click = "GetInfo">点击获取信息</button>
</template>
<script>
import {ref, reactive} from 'vue';
export default {
setup() {
function sendAJAX(url) {
return new Promise((resolve, reject) => {
const xhr = new XmlHttpRequset();
xhr.responseType = 'json';
xhr.open('GET', url);
xhr.send();
xhr.onreadystatechange = function{
if(xhr.readystate === 4) {
if(xhr.state >= 200 && xhr.state < 300) {
resolve(xhr.response);
}else{
reject(xhr.status);
}
}
}
}
};
//------------正文---------------
async function GetInfo() {
let Info = await sendAJAX('http://128.0.0.1:8000');
console.log(Info);
}
}
}
</script>
为什么还是觉得好麻烦呢 ????
其实主要还是这一段麻烦 其实这个不必担忧 等学完axios就好了 到时候好像可以直接干嘛干嘛的就发送AJAX请求 哎 对应了上文的一句话
Md 学!!!!!!
\