Promise和Async-Await的入门教程
1. Promise是什么
1.1 理解
抽象表达:
Promise 是一门新的技术(ES6 规范)
Promise 是 JS 中进行异步编程的新解决方案(旧方案是单纯使用回调函数)
具体表达:
从语法上来说: Promise 是一个构造函数
从功能上来说: promise 对象用来封装一个异步操作并可以获取其成功/失败的结果值
1.2 Promise的状态改变
Promise一种有三种状态,为 pending、fulfilled、rejected(未决定,履行,拒绝),同一时间只能存在一种状态,且状态一旦改变就不能再变。promise是一个构造函数,promise对象代表一项有两种可能结果(成功或失败)的任务,它还持有多个回调,出现不同结果时分别发出相应回调。
1.初始化,状态:pending
2.当调用resolve(成功),状态:pending=>fulfilled
3.当调用reject(失败),状态:pending=>rejected
状态的改变(或者说决议)不可逆,一旦决议就不能再更改。
1.3 为什么要用Promise
1)解决回调地狱
回调地狱:发送多个异步请求时,每个请求之间相互都有关联,会出现第一个请求成功后再做下一个请求的情况。我们这时候往往会用嵌套的方式来解决这种情况,但是这会形成"回调地狱"。如果处理的异步请求越多,那么回调嵌套的就越深。出现的问题:
1.代码逻辑顺序与执行顺序不一致,不利于阅读与维护。
2.异步操作顺序变更时,需要大规模的代码重构。
3.回调函数基本都是匿名函数,bug追踪困难。
doSomething(function(result) {doSomethingElse(result, function(newResult) {doThirdThing(newResult, function(finalResult) {console.log('Got the final result: ' + finalResult)}, failureCallback)}, failureCallback)
}, failureCallback)
Promise可以使用链式调用解决回调地狱的问题。
/*
使用 promise 的链式调用解决回调地狱
*/
doSomething().then(function(result) {return doSomethingElse(result)
})
.then(function(newResult) {return doThirdThing(newResult)
})
.then(function(finalResult) {console.log('Got the final result: ' + finalResult)
})
.catch(failureCallback)
2)解决异步
旧的回调函数:必须在启动异步任务前指定
promise:启动异步任务 => 返回promie对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
1.4 工作流程
1.5 代码初体验
1)fs读取文件
const fs = require('fs');
//回调函数 形式
// fs.readFile('./resource/content.txt', (err, data) => {
// // 如果出错 则抛出错误
// if(err) throw err;
// //输出文件内容
// console.log(data.toString());
// });
//Promise 形式
let p = new Promise((resolve , reject) => {fs.readFile('./resource/content.txt', (err, data) => {//如果出错if(err) reject(err);//如果成功resolve(data);});
});
//调用 then
p.then(value=>{console.log(value.toString());
}, reason=>{console.log(reason);
});
2)封装fs读取文件模块
/*** 封装一个函数 mineReadFile 读取文件内容* 参数: path 文件路径* 返回: promise 对象*/
function mineReadFile(path){return new Promise((resolve, reject) => {//读取文件require('fs').readFile(path, (err, data) =>{//判断if(err) reject(err);//成功resolve(data);});});
}
mineReadFile('./resource/content.txt')
.then(value=>{//输出文件内容console.log(value.toString());
}, reason=>{console.log(reason);
});
2. Promise API
2.1 构造函数
Promise 构造函数: Promise (excutor) {}
(1) executor 函数: 执行器 (resolve, reject) => {}
(2) resolve 函数: 内部定义成功时我们调用的函数 value => {}
(3) reject 函数: 内部定义失败时我们调用的函数 reason => {}
说明: executor 会在 Promise 内部立即同步调用,异步操作在执行器中执行
let p = new Promise((resolve, reject) => {// 同步调用console.log(111);//修改 promise 对象的状态reject('error');
});
2.2 then
Promise.prototype.then 方法: (onResolved, onRejected) => {}
(1) onResolved 函数: 成功的回调函数 (value) => {}
(2) onRejected 函数: 失败的回调函数 (reason) => {}
说明: 指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调返回一个新的 promise 对象
//调用then方法
p.then(value=>{console.log(value);
}, reason=>{console.warn(reason);
});
2.3 catch
Promise.prototype.catch 方法: (onRejected) => {}
(1) onRejected 函数: 失败的回调函数 (reason) => {}
说明: then()的语法糖, 相当于: then(undefined, onRejected)
let p = new Promise((resolve, reject) => {reject('error');
});
// console.log(222);
//执行 catch 方法
p.catch(reason => {console.log(reason);
});
2.4 resolve
Promise.resolve 方法: (value) => {}
(1) value: 成功的数据或 promise 对象
说明: 返回一个成功/失败的 Promise 对象
如果传入的参数为 非Promise类型的对象, 则返回的结果为成功Promise对象 如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果
let p1 = Promise.resolve(521);
// console.log(p1); 成功的Promise对象
//如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {// resolve('OK');reject('Error');
}));
// console.log(p2);
p2.catch(reason => {console.log(reason);
})
2.5 reject
Promise.reject 方法: (reason) => {}
(1) reason: 失败的原因
说明: 返回一个失败的 Promise 对象
// let p = Promise.reject(521);
// p2失败的结果是一个promise对象
let p2 = Promise.reject(new Promise((resolve, reject) => {resolve('OK');
}));
console.log(p2);
2.6 all
Promise.all 方法: (promises) => {}
(1) Promises: 包含 n 个 promise 的数组
(2) 成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
说明: 返回一个新的 Promise, 只有所有的 Promise 都成功才成功, 只要有一个失败了就直接失败
Promse.all在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示,在此之前只显示loading图标。
let p1 = new Promise((resolve, reject) => {resolve('OK');})let p2 = Promise.resolve('Success');// let p2 = Promise.reject('Error');let p3 = Promise.resolve('Oh Yeah');Promise.all([p1, p2, p3]).then((result) => {console.log(result) // [ 'OK', 'Success',"Oh Yeah" ]}).catch((error) => {console.log(error) // error})
2.7 race
Promise.race 方法: (promises) => {}
(1) Promises: 包含 n 个 Promise 的数组
说明: 返回一个新的 Promise, 第一个完成的 Promise 的结果状态就是最终的结果状态
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]);
//Promise的结果为 Success,因为p2为第一个完成的promise
console.log(result);
3. Promise关键问题
1)一个 Promise 指定多个成功/失败回调函数, 都会调用吗?
当 Promise 改变为对应状态时都会调用。
let p = new Promise((resolve, reject) => {resolve('OK');
});
///指定回调 - 1
p.then(value => {console.log(value);
});
//指定回调 - 2
p.then(value => {alert(value);
});
2)改变 Promise 状态和指定回调函数谁先谁后?
(1) 都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调
(2) 如何先改状态再指定回调?
① 在执行器中直接调用 resolve()/reject()
② 延迟更长时间才调用 then()
(3) 什么时候才能得到数据?
① 如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
② 如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
3)then方法的返回结果由什么决定
(1) 简单表达:
由 then()指定的回调函数执行的结果决定
(2) 详细表达:
① 如果抛出异常, 新 Promise 变为 rejected, reason 为抛出的异常
② 如果返回的是非 Promise 的任意值, 新 Promise 变为 resolved, value 为返回的值
③ 如果返回的是另一个新 Promise, 此 Promise 的结果就会成为新 Promise 的结果
let p = new Promise((resolve, reject) => {resolve('ok');
});
//执行 then 方法
let result = p.then(value => {// console.log(value);//1. 抛出错误// throw '出了问题';//2. 返回结果是非 Promise 类型的对象// return 521;//3. 返回结果是 Promise 对象// return new Promise((resolve, reject) => {// // resolve('success');// reject('error');// });
}, reason => {console.warn(reason);
});
console.log(result);
4)如何串联多个任务
(1) Promise 的 then()返回一个新的 promise, 可以开成 then()的链式调用
(2) 通过 then 的链式调用串连多个同步/异步任务
let p = new Promise((resolve, reject) => {setTimeout(() => {resolve('OK');}, 1000);
});
p.then(value => {return new Promise((resolve, reject) => {resolve("success");});
}).then(value => {console.log(value);
}).then(value => {console.log(value);
})
5)异常穿透
(1) 当使用 Promise 的 then 链式调用时, 可以在最后指定失败的回调
(2) 前面任何操作出了异常, 都会传到最后失败的回调中处理
let p = new Promise((resolve, reject) => {setTimeout(() => {resolve('OK');// reject('Err');}, 1000);
});
p.then(value => {// console.log(111);throw '失败啦!';
}).then(value => {console.log(222);
}).then(value => {console.log(333);
}).catch(reason => {console.warn(reason);
});
6)中断Promise链条
(1) 当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数
(2) 办法: 在回调函数中返回一个 pendding 状态的 promise 对象
let p = new Promise((resolve, reject) => {setTimeout(() => {resolve('OK');}, 1000);
});
p.then(value => {console.log(111);//有且只有一个方式return new Promise(() => {});
}).then(value => {console.log(222);
}).then(value => {console.log(333);
}).catch(reason => {console.warn(reason);
});
4. Async-Await
4.1 Async
函数的返回值为 Promise 对象
Promise 对象的结果由 async 函数执行的返回值决定
async必须声明的是一个function
async function main(){//1. 如果返回值是一个非Promise类型的数据// return 521;//2. 如果返回的是一个Promise对象// return new Promise((resolve, reject) => {// resolve('OK');// // reject('Error');// });//3. 抛出异常throw "Oh NO";
}
let result = main();
console.log(result);
sync声明的函数的返回本质上是一个Promise。所以你想像正常function
的返回那样,拿到返回值,你可以这样拿到返回值:
main().then(result=>{console.log(result);
})
4.2 Await
1)await 右侧的表达式一般为 promise 对象, 但也可以是其它的值
2)如果表达式是 promise 对象, await 返回的是 promise 成功的值(await会等待promise返回结果后,再继续执行)
3)如果表达式是其它值, 直接将此值作为 await 的返回值(会立即执行,不过就没有意义了)
async function main(){let p = new Promise((resolve, reject) => {// resolve('OK');reject('Error');})//1. 右侧为promise的情况// let res = await p;//2. 右侧为其他类型的数据// let res2 = await 20;// 可以用标准的try catch语法捕捉错误try{let res3 = await p;console.log(res3);}catch(e){console.log(e);}
}
main();
注意:
await 必须写在 async 函数中, 但 async 函数中可以没有 await
await等待的虽然是Promise对象,但不必写 .then(...),直接可以得到返回值。
如果 await 的 promise 失败了, 就会抛出异常, 需要通过 try...catch 捕获处理
4.3 async/await的中断
首先我们要明确的是,Promise
本身是无法中止的,Promise
本身只是一个状态机,存储三个状态(pending,resolved,rejected),一旦发出请求了,必须闭环,无法取消,之前处于pending状态只是一个挂起请求的状态,并不是取消,一般不会让这种情况发生,只是用来临时中止链式的进行。
中断(终止)的本质在链式中只是挂起,并不是本质的取消Promise
请求,那样是做不到的,Promise
也没有cancel
的状态。
不同于Promise
的链式写法,写在async/await中想要中断程序就很简单了,因为语义化非常明显,其实就和一般的function
写法一样,想要中断的时候,直接return
一个值就行,null
,空,false
都是可以的。看例子:
const setDelay = (millisecond) => {return new Promise((resolve, reject)=>{if (typeof millisecond !== 'number') reject(new Error('参数必须是number类型'));setTimeout(()=> {resolve(`我延迟了${millisecond}毫秒后输出的`)}, millisecond)})}
let count = 6;const demo = async ()=>{const result = await setDelay(3000);console.log(result);if (count > 5) {return '我退出了,下面的不进行了';// return;// return false; // 这些写法都可以// return null;// return Promise.reject(new Error('拒绝'));}console.log(await setDelay(1000));console.log('完成了');};demo().then(result=>{console.log(result);}).catch(err=>{console.log(err);})
实质就是直接return
返回了一个Promise
,相当于return Promise.resolve('我退出了下面不进行了')
,当然你也可以返回一个“拒绝”:return Promise.reject(new Error('拒绝'))
那么就会进到错误信息里去。
async函数实质就是返回一个Promise!
4.4 async和await的结合
下面我们用两个案例演示一下:
1)读取多个文件,并输出
const fs = require('fs');
const util = require('util');
const mineReadFile = util.promisify(fs.readFile);
//async 与 await
async function main(){try{//读取第一个文件的内容let data1 = await mineReadFile('./resource/1.html');let data2 = await mineReadFile('./resource/2.html');let data3 = await mineReadFile('./resource/3.html');console.log(data1 + data2 + data3);}catch(e){console.log(e);}
}
main();
2)发送ajax请求
<button id="btn">点击获取段子</button><script>function sendAJAX(url){return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.responseType = 'json';xhr.open("GET", url);xhr.send();//处理结果xhr.onreadystatechange = function(){if(xhr.readyState === 4){//判断成功if(xhr.status >= 200 && xhr.status < 300){//成功的结果resolve(xhr.response);}else{reject(xhr.status);}}}});}
//段子接口地址 https://api.apiopen.top/getJokelet btn = document.querySelector('#btn');
btn.addEventListener('click',async function(){//获取段子信息let duanzi = await sendAJAX('https://api.apiopen.top/getJoke');console.log(duanzi);});</script>
Promise和Async-Await的入门教程相关推荐
- es6 --- promise和async/await的区别
首先需要了解async函数: async是Generator函数的语法糖: // 使用Generator依次读取两个文件 var fs = require('fs'); var readFile = ...
- 解决异步问题,教你如何写出优雅的promise和async/await,告别callback回调地狱!
解决异步问题--promise.async/await 一.单线程和异步 1.单线程是什么 2.为什么需要异步 3.使用异步的场景 二.promise 1.promise的三种状态 2.三种状态的表现 ...
- setTimeout、setInterval、promise、async/await的顺序详解(多种情况,非常详细~)
本文很长,列举的情况很多. 在阅读本文之前,如果您有充足的时间,请新建一个项目与本文一同实践. 每段代码都有对应的解释,但是自己动手尝试印象才会更深哦~ setInterval:表示多久执行一次,需要 ...
- promise 和 async await区别
什么是Async/Await? async/await是写异步代码的新方式,以前的方法有回调函数和Promise. async/await是基于Promise实现的,它不能用于普通的回调函数. as ...
- 一眼看懂promise与async await的区别
// promise方法let p1 = new Promise((resolve,reject) => {setTimeout(() => {resolve('我是p1')},4000) ...
- promise 、async/await 的原理及实现
前言 事件循环机制 由于 javascript 引擎是采用单线程运行机制,执行耗时过大的操作时会造成页面的阻塞,为了解决页面的阻塞问题,js 将任务分为 同步任务.异步任务,随之而来的是异步带来的执行 ...
- 明明有了 promise ,为啥还需要 async await ?
作者 | Angus安格斯 来源 | https://juejin.cn/post/6960855679208783903 为了让还没听说过这个特性的小伙伴们有一个大致了解,以下是一些关于该特性的简要 ...
- Async/Await替代Promise的6个理由
2019独角兽企业重金招聘Python工程师标准>>> 译者按: Node.js的异步编程方式有效提高了应用性能:然而回调地狱却让人望而生畏,Promise让我们告别回调函数,写出更 ...
- Promise async/await的理解和用法
Promise && async/await的理解和用法 为什么需要promise(承诺)这个东西 在之前我们处理异步函数都是用回调这个方法,回调嵌套的时候会发现 阅读性 和 调试 的 ...
- promise用法_JavaScript中的async/await的用法和理解
昨天更新的是"JavaScript中的Promise使用详解",其实也就是说了下基本用法和自己对Promise的理解,可能有错误之处,也欢迎指出.今天就说一说"JavaS ...
最新文章
- PyTorch迎来5岁生日,创始人带领大一实习生开发:没想到会这么成功
- how to create view (windows)
- 分析 C# 2.0 新特性 -- 范型(Generics)
- 使用miniSIPServer搭建pjsip服务器
- HDU 2874 Connections between cities(LCA离线算法实现)
- 轮盘赌算法的java实现算例
- 视频工作者应该知道的几个网站
- go tour - Go 入门实验教程
- ORACLE解决表空间不释放空间
- 仙之侠道2玖章各个任务详情_仙之侠道2玖章
- 重写虫虫项目犯的低级错误
- xpath获取不包含某一子标签的a标签
- 六Elasticsearch之中文分词器插件es-ik的热更新词库
- 三月计算机二级,2017年3月计算机二级考试MSOffice冲刺题与答案
- JS -- 对于JQuery中 append 方法的理解
- BFS-BZOJ-1615-[Usaco2008 Mar]The Loathesome Hay Baler麻烦的干草打包机
- 一、ADSP-21489开发版在VisualDSP软件下的仿真器配置
- Visual C# 的DirectX开发系列一初识DirectX
- android studio 自定义控件
- 系统分析师真题2019试卷相关概念二