1 Promise 的理解
1.1 promise 是什么
1.1.1 理解
-
抽象表达
- Promise 是一门新的技术(ES6 规范)
- Promise 是 JS 中进行一部编程的新的解决方案(旧方案是单纯使用回调函数)
-
具体表达
- 从语法上来说:Promise 是一个构造函数
- 从功能上来说:Promise 对象用来封装一个一部操作并可以获取其成功/失败的结果值
1.1.2 异步编程的例子
-
fs 文件操作(fs 为 node.js 下的一个模块,可以对磁盘进行读写操作)
js1const fs = require('fs') 2/* 3 fs.readFile('./resource/content.txt',(err,data)=>{ 4 //如果错误则抛出错误 5 if(err) throw err; 6 //否则输出文件内容 7 console.log(data.toString()); 8 }); 9 */ 10 11//Promise形式 12let p = new Promise((resolve, reject) => { 13 fs.readFile('./resource/content.txt', (err, data) => { 14 //如果错误则抛出错误 15 if (err) reject(err) 16 //如果成功 17 resolve(data) 18 }) 19}) 20 21//调用then方法 22p.then( 23 (value) => { 24 console.log(value.toString()) 25 }, 26 (reason) => { 27 console.log(reason) 28 } 29)
-
数据库操作(mongoDB,MySQL 等)
-
AJAX
js1$.get('/server', (data) => {})
-
定时器
js1setTimeout(() => {}, 2000)
1.2 为什么要使用 Promise
1.2.1 指定回调函数的方式更加灵活
- 旧的:必须在启动异步任务之前指定
- promise:启动异步 => 返回 promise 对象 => 给 promise 对象绑定回调函数(甚至可以在异步任务结束后指定多个)
1.2.2 支持链式调用,可以解决回调地狱问题
- 回调地狱:回调函数嵌套调用,外部回调函数异步执行的 结果是嵌套的回调执行的条件
- 回调地狱的缺点
- 不便于阅读
- 不便于异常处理
- 解决方案:promise 链式调用
- 终极解决方案:async/await
1.3 Promise 的状态
- 实例对象中的一个属性 『PromiseState』
- pending 未决定的
- resolved / fullfilled 成功
- rejected 失败
- promise 的状态改变
- pending 变为 resolved/fullfilled
- pending 变为 rejected
-
只有这两种变化可能,也就是说不可能由 resolved 变为 rejected,且一个 promise 对象只能改变一次
-
无论变为成功还是失败,都会有一个结果数据
-
成功的结果数据一般称为 value,失败的结果数据一般称为 reason
1.4 Promise 对象的值
实例对象中的另一个属性 『PromiseResult』 保存着异步任务『成功/失败』的结果,下面两个函数可以修改对返回的结果进行修改,其他皆不可以
- resolve()
- reject ()
2 Promise 的使用
2.1 API
- Promise 构造函数:Promise(excutor){}
- executor 函数:执行器(resolve,reject) => {}
- resolve 函数:内部定义成功时我们调用的函数 value => {}
- reject 函数:内部定义失败时我们调用的函数 reason => {}
- 说明:executor 会在 Promise 内部立即同步调用,异步操作在执行器中执行
js1let p = new Promise((resolve, reject) => {
2 //同步调用
3 console.log(111)
4})
5console.log(222)
- Promise.prototype.then 方法:(onResolved,onReject) => {}
- onResolved 函数:成功的回调函数 (value) => {}
- onReject 函数:失败的回调函数 (reason) => {}
- 说明:指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调返回一个新的 promise 对象
- Promise.prototype.catch 方法:(onRejected) => {}
- onRejected 函数:失败的回调函数 (reason) =>{}
-
说明:then()的语法糖,相当于:then(undefined,onRejected)
js1let p = new Promise((resolve, reject) => { 2 //修改promise对象的状态 3 reject('error') 4}) 5//执行catch方法 6p.catch((reason) => { 7 console.log(reason) //输出error 8})
- Promise.resolve 方法:(value) => {}
- value:成功的数据或 promise 对象
-
说明:返回一个成功/失败的 promise 对象
js1let p1 = Promise.resolve(123) 2//如果传入的参数为非Promise类型的对象,则返回的结果皆为成功的promise对象 3console.log('p1:', p1) 4 5//如果传入的参数为Promise对象,则参数的结果决定resolve的结果 6let p2 = Promise.resolve( 7 new Promise((resolve, reject) => { 8 //成功的结果 9 resolve('OK') 10 }) 11) 12console.log('p2:', p2) 13 14let p3 = Promise.resolve( 15 new Promise((resolve, reject) => { 16 //失败的结果 17 reject('error') 18 }) 19) 20console.log('p3:', p3)
- Promise.reject 方法: (reason) => {}
- reason:失败的原因或 promise 对象
-
说明: 返回一个失败的 promise ==对象==
js1let p1 = Promise.reject(456) 2//如果传入的参数为非Promise类型的对象,则返回的结果皆为成功的promise对象 3console.log('p1:', p1) 4 5//如果传入的参数为Promise对象,则参数的结果决定resolve的结果 6let p2 = Promise.reject( 7 new Promise((resolve, reject) => { 8 resolve('OK') 9 }) 10) 11//即使传入的时一个成功的promise对象,返回的也是失败的promise对象 12console.log('p2:', p2) //结果PromiseResult是传入的参数OK 13 14let p3 = Promise.reject( 15 new Promise((resolve, reject) => { 16 //失败的结果 17 reject('error') 18 }) 19) 20console.log('p3:', p3)
- Promise.all 方法:(promises) =>{}
- promises:包含 n 个 promise 的数值
-
说明:返回一个新的 promise,只有所有的 promise 都成功了才成功,只有有一个失败了就直接失败
js1let p1 = new Promise((resolve, reject) => { 2 resolve('OK') 3}) 4let p2 = Promise.resolve('Success') 5let p3 = Promise.resolve('Oh Yeah') 6let p4 = Promise.reject('Error') //失败的 7 8//成功的情况 9const resultSuccess = Promise.all([p1, p2, p3]) 10console.log('resultSuccess:', resultSuccess) 11 12//失败的情况 13const resultError = Promise.all([p1, p2, p4]) 14console.log('resultError:', resultError)
- Promise.race 方法:(promises) =>{}
- promises:包含 n 个 promise 的数组
- 说明:返回一个新的 promise,第一个完成的 promise 的结果状态就是最终的结果状态
js1let p1 = new Promise((resolve, reject) => {
2 resolve('OK')
3})
4let p2 = Promise.resolve('Success')
5let p3 = Promise.resolve('Oh Yeah')
6
7let p4 = new Promise((resolve, reject) => {
8 //添加定时器,异步任务
9 setTimeout(() => {
10 resolve('OK')
11 }, 1000)
12})
13let p5 = Promise.reject('Error') //失败的结果
14//调用
15const result1 = Promise.race([p1, p2, p3])
16console.log('result1', result1)
17
18const result2 = Promise.race([p4, p2, p3])
19console.log('result2', result2)
20
21const result3 = Promise.race([p4, p5, p1])
22console.log('result3', result3)
23
24const result4 = Promise.race([p5, p1, p2])
25console.log('result4', result4)
2.2 Promise 的几个关键问题
-
如何改变 promise 的状态
- resolve(value):如果当前是 pending 就会变为 resolved
- reject(reason): 如果当前是 pending 就会变为 rejected
- 抛出异常: 如果当前是 pending 就会变为 rejected
-
一个 promise 指定多个成功/失败回调函数, 都会调用吗?
- 当 promise 改变为对应状态时都会调用
js1let p = new Promise((resolve, reject) => { 2 resolve('OK') //改变状态 3}) 4 5///指定回调 - 1 6p.then((value) => { 7 console.log(value) 8}) 9 10//指定回调 - 2 11p.then((value) => { 12 alert(value) 13})
- 改变 promise 状态和指定回调函数谁先谁后?
(1)都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再==指定==回调
js1//先改变状态,再指定回调
2let p1 = new Promise((resolve, reject) => {
3 resolve('OK') //同步任务
4})
5
6p1.then(
7 (value) => {
8 console.log(value)
9 },
10 (reason) => {}
11)
12
13//先指定回调,再改变状态
14let p2 = new Promise((resolve, reject) => {
15 //异步任务
16 setTimeout(() => {
17 resolve('OK')
18 }, 3000)
19})
20
21//then先执行,而不是then里面的回调函数先执行,所以value会再3s后输出
22p2.then(
23 (value) => {
24 console.log(value) //状态改变才能拿到数据
25 },
26 (reason) => {}
27)
(2)如何先改状态再指定回调?
-
在执行器中直接调用 resolve()/reject()
-
延迟更长时间才调用 then()
(3) 什么时候才能得到数据?
- 如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
- 如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
- promise.then()返回的新 promise 的结果状态由什么决定?
-
简单表达: 由 then()指定的回调函数执行的结果决定
-
详细表达:
- 如果抛出异常,新 promise 变为 rejected,reason 为抛出的异常
- 如果返回的是非 promise 的任意值,新 promise 变为 resolved,value 为返回值
- 如果返回的是另一个新的 promise,此 promise 的结果就会成为新 promise 的结果
js1let p = new Promise((resolve, reject) => {
2 resolve('ok')
3})
4//执行 then 方法
5let result = p.then(
6 (value) => {
7 // console.log(value);
8 //1. 抛出错误
9 // throw '出了问题';
10 //2. 返回结果是非 Promise 类型的对象
11 // return 123;
12 //3. 返回结果是 Promise 对象
13 return new Promise((resolve, reject) => {
14 resolve('success')
15 // reject('error');
16 })
17 },
18 (reason) => {
19 console.warn(reason)
20 }
21)
22
23console.log(result)
- promise 如何串连多个操作任务?
-
promise 的 then()返回一个新的 promise, 可以看成 then()的链式调用
-
通过 then 的链式调用串连多个同步/异步任务
js1let p = new Promise((resolve, reject) => { 2 setTimeout(() => { 3 resolve('OK') 4 }, 1000) 5}) 6 7p.then((value) => { 8 return new Promise((resolve, reject) => { 9 resolve('success') 10 }) 11}) 12 .then((value) => { 13 console.log(value) //success 14 }) 15 .then((value) => { 16 console.log(value) 17 //undefined,因为第二个then没有return,所以return undefined,所以 18 //第二个then的返回结果是一个成功的promise且成功的结果是undefined,而不是返回一个promise对象 19 //所以第三个then会输出第二个then返回的结果 20 })
- promise 异常传透?
- 当使用 promise 的 then 链式调用时, 可以在最后指定失败的回调,前面任何操作出了异常, 都会传到最后失败的回调中处理
js1let p = new Promise((resolve, reject) => {
2 setTimeout(() => {
3 resolve('OK')
4 // reject('Err');
5 }, 1000)
6})
7
8p.then((value) => {
9 // console.log(111);
10 throw '失败啦!'
11})
12 .then((value) => {
13 console.log(222)
14 })
15 .then((value) => {
16 console.log(333)
17 })
18 .catch((reason) => {
19 //catch方法最后对错误进行统一的处理
20 console.warn(reason)
21 })
- 中断 promise 链?
-
当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数
-
办法: 在回调函数中返回一个 pendding 状态的 promise 对象(有且只有一个方式)
js1let p = new Promise((resolve, reject) => { 2 setTimeout(() => { 3 resolve('OK') 4 }, 1000) 5}) 6 7p.then((value) => { 8 console.log(111) 9 //有且只有一个方式 10 return new Promise(() => {}) //返回以恶pending状态的promise对象,状态不改变,后面的回调不再执行 11}) 12 .then((value) => { 13 console.log(222) 14 }) 15 .then((value) => { 16 console.log(333) 17 }) 18 .catch((reason) => { 19 console.warn(reason) 20 })
3 自定义(手写)Promise
这部分看文件夹中的代码
4 async 与 await
4.1 MDN 文档
4.2 async 函数
- 函数的返回值为 promise 对象
- promise 对象的结果由 async 函数执行的返回值决定
js1//then
2async function main() {
3 //1. 如果返回值是一个非Promise类型的数据
4 // return 521;
5 //2. 如果返回的是一个Promise对象
6 // return new Promise((resolve, reject) => {
7 // // resolve('OK');
8 // reject('Error');
9 // });
10 //3. 抛出异常
11 throw 'Oh NO'
12}
13
14let result = main()
15
16console.log(result)
4.3 await 表达式
- await 右侧的表达式一般为 promise 对象, 但也可以是其它的值
- 如果表达式是 promise 对象, await 返回的是 promise 成功的值
- 如果表达式是其它值, 直接将此值作为 await 的返回值
js1async function main() {
2 let p = new Promise((resolve, reject) => {
3 // resolve('OK');
4 reject('Error')
5 })
6 //1. 右侧为promise的情况,await 返回的是 promise 成功的值
7 // let res = await p;
8 // console.log(res);
9
10 //2. 右侧为其他类型的数据,直接将此值作为 await 的返回值
11 let res2 = await 20
12 console.log(res2)
13
14 //3. 如果promise是失败的状态,用try catch进行捕获
15 try {
16 let res3 = await p
17 } catch (e) {
18 console.log(e)
19 }
20}
21
22main()
4.4 注意
- await 必须写在 async 函数中, 但 async 函数中可以没有 await
- 如果 await 的 promise 失败了, 就会抛出异常, 需要通过 try...catch 捕获处理
4.5 async 与 await 结合使用
js1/**
2 * resource 1.html 2.html 3.html 文件内容
3 */
4
5const fs = require('fs')
6const util = require('util')
7const mineReadFile = util.promisify(fs.readFile)
8
9// //回调函数的方式
10// fs.readFile('./resource/1.html', (err, data1) => {
11// if(err) throw err;
12// fs.readFile('./resource/2.html', (err, data2) => {
13// if(err) throw err;
14// fs.readFile('./resource/3.html', (err, data3) => {
15// if(err) throw err;
16// console.log(data1 + data2 + data3);
17// });
18// });
19// });
20
21//async 与 await
22async function main() {
23 try {
24 //读取第一个文件的内容
25 let data1 = await mineReadFile('./resource/1.html')
26 let data2 = await mineReadFile('./resource/2.html')
27 let data3 = await mineReadFile('./resource/3.html')
28 console.log(data1 + data2 + data3)
29 } catch (e) {
30 console.log(e.code)
31 }
32}
33
34main()
5 参考
6 代码
喜欢这篇文章吗?
加载中...
评论
0 条登录后即可参与评论讨论
加载评论中...
相关文章
目录