继es6+新增的方法,Promise 也是很重要的一趴技术
Promise (非常重要)
前置理解:同步回调与异步回调
// 异步定时器
setTimeout(() => { // 异步回调:不会立即执行,会等到初始同步代码都执行完后才会执行
console.log('定时器回调执行')
}, 0)
console.log('setTimeout之后');
// 同步遍历
[1, 2, 3, 4].forEach(item => { // 同步回调:会立即执行,只有回调函数都执行完后工作(遍历)才结束
console.log('遍历数组的回调执行', item)
})
console.log('forEach()之后')
Promise理解
- Promise 是异步编程的一种解决方案,比传统的解决方案(纯回调函数)更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了
Promise
对象 - 多层回调函数嵌套被称作回调地狱:代码层层嵌套,可阅读性差,非常不便于维护。
- Promise可以将异步操作以同步的流程表达出来, 避免了回调函数的层层嵌套(俗称’回调地狱’)
/*
演示回调地狱
请求数据a
成功后,请求数据b
成功后,请求数据c
成功后,显示数据c
使用打印和定时器模拟
*/
console.log('发请求获取数据a')
setTimeout(() => {
console.log('请求数据a成功了,发请求获取数据b')
setTimeout(() => {
console.log('请求数据b成功了, 发请求获取数据c')
setTimeout(() => {
console.log('请求数据c成功了, 显示数据c')
}, 1000)
}, 1000)
}, 1000)
// 演示回调地狱的伪代码
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult)
}, failureCallback)
}, failureCallback)
}, failureCallback)
Promise入门
-
Promise
是一个构造函数,自己身上有resolve
、reject
、all
这几个眼熟的静态方法,原型上有then
、catch
等同样很眼熟的实例方法 -
Promise构造函数, 接收一个称为executor/执行器的回调函数,
-
Promise内部会立即调用executor,并传入2个函数,一般分别称为resolve与reject
-
在executor函数中,我们一般都会立即启动异步任务(比如发ajax请求,启动定时器任务),
- 如果异步任务成功,调用resolve并指定成功的value
- 如果异步任务失败,调用reject并指定失败的reason
-
Promise
对象的状态- 3种状态
-
待定(pending): 初始状态,既没有成功,也没有失败。
- 成功(fulfilled/resolved): 意味着操作成功完成。
- 失败(rejected): 意味着操作失败。
- 2种状态变化
- pending ==> fulfilled resolve()
- pending ==> rejected reject()
- 1次状态变化
-
一个promise对象只能发生1次状态变化
-
要么pending ==> fulfilled,要么pending ==> rejected
const p = new Promise((resolve, reject) => { console.log('执行器函数回调了') // 启动异步任务 setTimeout(() => { const success = Date.now() % 2 === 1 // 假设:当前时间是奇数代表成功,否则失败 // 如果成功了, 调用resolve指定成功的value if (success) { resolve('成功的数据') } else { // 如果失败了,调用reject指定失败的reason reject('失败的原因') } }, 2000) }) console.log('new Promise()之后') // 立即查看p console.log(p) // 2s之后查看p setTimeout(() => { console.log(p) }, 2000)
-
Promise对象的then和catch方法
-
语法:
- Promise.prototype.then(onResolved, onRejected)
- onResolved:指定成功的回调,用于异步接收成功的value
- onRejected:指定失败的回调, 用于异步接收失败的reason
- 返回值是一个新的promise对象
- Promise.prototype.catch(onRejected)
- onRejected:指定失败的回调, 用于异步接收失败的reason
- 返回值是一个新的promise对象
- Promise.prototype.then(onResolved, onRejected)
-
作用:
- 用于在promise对象外部获取promise内部执行的异步任务的结果(成功的或失败的)
- 承载新的异步任务(也可以是同步任务)
-
注意:
-
onResolved和onRejected都是异步回调
-
catch本质是then(null, onRejected)的语法糖
const p = new Promise((resolve, reject) => { // 执行器函数 ==》执行异步任务 // 调动异步任务 setTimeout(() => { // 如果成功了, 调用resolve ==> promise对象的状态会从pending变为fulfilled ==> then()指定的成功回调就会调用 // resolve(1) // 如果失败了,调用reject reject(2) }, 2000) }) p.then( value => { // 用于接收成功value的回调onResolved console.log('onResolved', value) }, reason => { // 用于接收失败reason的回调onRejected console.log('onRejected', reason) } ) console.log('p.then()之后', p) new Promise((resolve, reject) => { reject(3) }).catch(reason => { // 只能指定失败的回调onRejected console.log('catch onRejected', reason) })
-
then方法返回的新promise对象结果状态如何确定?
-
简洁表达:由then指定的回调函数执行的结果决定
-
详细表达:4种情况
-
如果抛出了错误 ==》 失败,reason为抛出的错误
-
如果返回失败的promise ==> 失败, reason为返回的失败promise的reason
-
如果返回成功的promise ==> 成功, value为返回的成功promise的value
-
其它所有情况 ==》 成功,value为函数返回的结果
/* - 简洁表达:由then指定的回调函数执行的结果决定 - 详细表达:4种情况 - 1. 如果抛出了错误 ==》 失败,reason为抛出的错误 - 2. 如果返回失败的promise ==> 失败, reason为返回的失败promise的reason - 3. 如果返回成功的promise ==> 成功, value为返回的成功promise的value - 4. 其它所有情况 ==》 成功,value为函数返回的结果 */ new Promise((resolve, reject) => { // resolve(1) reject(2) }).then( value => { console.log('onResolved1', value) }, reason => { console.log('onRejected1', reason) // 1. 如果抛出了错误 ==》 失败,reason为抛出的错误 // throw 3 // 2. 如果返回失败的promise ==> 失败, reason为返回的失败promise的reason // return new Promise((resolve, reject) => { // reject(4) // }) // 3. 如果返回成功的promise ==> 成功, value为返回的成功promise的value // return new Promise((resolve, reject) => { // resolve(5) // }) // 4. 其它所有情况 ==》 成功,value为函数返回的结果 // return 6 } ).then( value => { console.log('onResolved2', value) }, reason => { console.log('onRejected2', reason) } ).catch( reason => { console.log('onRejected3', reason) } ).then( value => { console.log('onResolved4', value) }, reason => { console.log('onRejected4', reason) } )
-
利用Promise链式调用解决回调地狱问题
// 演示回调地狱的伪代码
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult)
}, failureCallback)
}, failureCallback)
}, failureCallback)
// 利用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)
Promise 测试
console.log('start')
const promise = new Promise((resolve, reject) => {
reject();
console.log(222)
})
promise
.then(() => {
console.log(333)
return new Promise((resolve) => {
reject();
})
})
.catch(() => {
console.log(444)
})
.then(() => {
console.log(555)
return new Promise((reject, resolve) => {
reject()
// resolve()
})
})
.catch(() => {
console.log(666)
throw new Error("报错了~")
})
.then(() => {
console.log(777)
})
console.log('end')
Promise对象的finally方法
promise变成成功/失败都触发,pending不触发
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
console.log('onResolved', value)
// throw 3
},
reason => {
console.log('onRejected', reason)
}
).finally(() => { // promise变成成功/失败都触发,pending不触发
console.log("finally")
})
Promise的静态方法
-
Promise.resolve():创建一个成功promise的简洁语法
-
Promise.reject():创建一个失败promise的简洁语法
-
Promise.all([promise1, …]) 传入n个promise对象,只有n个promise对象的状态都成功,才成功,只要有一个失败,就失败
/* 创建一个成功的value为1的promise对象 */ const p1 = new Promise((resolve, reject) => { resolve(1) }) console.log(p1) const p2 = Promise.resolve(1) console.log(p2) /* 创建一个失败的reason为 new Error('请求失败') 的promise对象 */ const p3 = new Promise((resolve, reject) => { reject(new Error('请求失败')) }) console.log(p3) const p4 = Promise.reject(new Error('请求失败')) console.log(p4) const p5 = new Promise((resolve, reject) => { setTimeout(() => { resolve(1) }, 1000) }) const p6 = new Promise((resolve, reject) => { setTimeout(() => { resolve(2) // reject(-2) }, 2000) }) const p7 = Promise.all([p5, p6]) p7.then( values => { console.log('异步任务都成功了', values) }, reason => { console.log('有异步任务失败了', reason) } )
Generator (了解)
什么是Generator(生成器)
-
Generator 函数是 ES6 提供的一种新的异步编程解决方案,内部封装了很多的状态,被称作状态机/生成器
-
执行Generator会返回一个迭代器对象(iterator),使用iterator来遍历出Generator内部的状态
-
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,
function
关键字与函数名之间有一个星号;二是,函数体内部使用yield
表达式,定义不同的内部状态(yield
在英语里的意思就是“产出”)/* 定义generator函数 */ function* gen() { yield "状态1" yield "状态2" yield "状态3" yield "状态4" } /* 调用了generator后返回的是迭代器对象 每次调用next()就可以得到yield右侧表达式的值 */ let it = gen() console.log(it) console.log(it.next()) console.log(it.next()) console.log(it.next()) console.log(it.next()) console.log(it.next())
Generator的注意事项
-
调用generator函数返回的是迭代器对象,在generator函数中,遇到yield就会停止,直到运行next
-
可以使用for of执行gen
-
对象没有Iterator接口,可以手动部署一个
var myIterable = {} myIterable[Symbol.iterator] = function* () { yield 1 yield 2 yield 3 } for (let prop of myIterable) { console.log(prop) }
Generator的练习
<!--点击提示剩余次数-->
<button id="btn">点击抽奖</button>
<script>
const oBtn = document.getElementById("btn")
const start = gen()
oBtn.onclick = function () {
start.next()
}
function draw(count) {
alert("还剩" + (count - 1) + "次机会")
}
function* gen() {
//..
yield draw(5)
//..
yield draw(4)
//..
yield draw(3)
//..
yield draw(2)
//..
yield draw(1)
}
</script>
<script>
/*
需求:请求a数据,再请求b数据,请求c数据
*/
function* fn() {
yield setTimeout(() => {
console.log("a数据成功了")
iteratorObj.next() // 执行下一段代码
}, 1000)
yield setTimeout(() => {
console.log("b数据成功了")
iteratorObj.next() // 执行下一段代码
}, 2000)
yield setTimeout(() => {
console.log("c数据成功了")
iteratorObj.next(); // 执行下一段代码
}, 3000)
console.log('全部数据请求回来~')
}
const iteratorObj = fn()
iteratorObj.next()
</script>
async和await (重要)
- ES2017规范指定的新的异步编码方式
- async 用于申明一个 function 是异步的,而 await 用于等待一个异步函数执行完成
- 是 Generator的语法糖: async取代Generator函数的星号*,await取代Generator的yield
- 是异步编程的“终极”解决方案:编码简洁, 可阅读性好
async
-
语法:
async function foo(){
await 异步操作1;
await 异步操作2;
}
-
async 函数会返回一个 Promise 对象,这个promise对象的结果状态由函数体执行的结果决定
async function fn() { console.log("fn函数执行了") return 333 }
await
-
不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行
-
await
是个运算符,await 表达式的运算结果取决于它等的东西 -
如果它等到的不是一个 Promise 对象,那 await 表达式的结果就是表达式本身的结果
- 如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
- 如果promise执行结果为reject,则await无返回值, 需要使用try…catch来处理错误
-
async 函数会返回一个 Promise 对象,这个promise对象的结果状态由函数体执行的结果决定
-
抛出错误 ==》 失败, 且reason为抛出的错误
- 返回一个失败的promise ==》 失败,且reason为失败promise的reason
-
返回一个成功的promise ==> 成功, 且value为成功promise的value
-
其它所有情况 ==》成功, 且value为函数体返回的结果
async function fn() { console.log("fn函数执行了") await 123 // 不等 await setTimeout(() => { // 不等 console.log(222); }, 1000) } async function fn() { console.log("333") const re2 = await new Promise((resolve, reject) => { setTimeout(function () { console.log(222) reject("heng") }, 2000) }) console.log(111) console.log(re2) return "hello" // 返回成功结果值 } const re = fn() re.catch((error) => { console.log(error) }) //解决await的reject状态报错 re.then((data) => { console.log(data) }) //data是成功结果 函数返回值 console.log(re);
-
-
练习:请求a数据,请求成功再请求b数据
/* 请求a数据,请求成功再请求b数据 */ async function fn() { await new Promise((resolve) => { setTimeout(() => { console.log('a异步操作完成') resolve() }, 1000) }) console.log(2) await new Promise((resolve) => { setTimeout(() => { console.log('b异步操作完成') resolve() }, 1000) }) } fn() console.log(1) // 上面的代码,执行的效果类似于下面的代码: function* fn() { yield setTimeout(() => { console.log('a异步操作完成') it.next() }, 1000) console.log(2) yield setTimeout(() => { console.log('b异步操作完成') it.next() }, 1000) } const it = fn() it.next() console.log(1)
JS的宏队列与微队列
- JS事件循环机制实现中用来存储待执行回调函数的队列有2个:宏队列与微队列
- 宏队列:setTimeout回调 / setInterval回调 / DOM事件回调 / ajax回调
- 微队列:Promise / async&await / MutationObserver
- 执行顺序规则:每次要执行宏队列里的第一个任务之前,先要执行完微队列里所有的微任务
/*
宏队列: [宏任务1,宏任务2.....]
微队列: [微任务1,微任务2.....]
规则:每次要执行宏队列里的第一个任务之前,先要执行完微队列里所有的微任务
*/
console.log('start')
setTimeout(() => {
console.log('定时器回调执行1')
Promise.resolve().then(() => {
console.log('onResolved回调执行3')
})
}, 0)
setTimeout(() => {
console.log('定时器回调执行2')
}, 0)
Promise.resolve().then(() => {
console.log('onResolved回调执行1')
})
Promise.resolve().then(() => {
console.log('onResolved回调执行2')
})
console.log('end')
自定义(手写) Promise
1. 定义整体语法结构
/*
构造函数
excutor: 内部立即同步执行的函数 (resolve, reject) => {}
*/
function Promise(excutor) {
}
/*
用于指定成功和失败回调
方法的返回值是一个新的promise
*/
Promise.prototype.then = function (onResolved, onRejected) {
}
/*
用于指定失败回调
方法的返回值是一个新的promise
*/
Promise.prototype.catch = function (onRejected) {
}
/*
返回一个指定value的成功promise
*/
Promise.resolve = function (value) {
}
/*
返回一个指定reason的失败promise
*/
Promise.reject = function (reason) {
}
/*
返回一个promise,
promises所有promise都成功了, 才最终成功
只要promises中有一个失败了, 就直接失败
*/
Promise.all = function (promises) {
}
2. 实现构造函数
-
Promise.js
const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECTED = 'rejected' /* 构造函数 excutor: 内部立即同步执行的函数 (resolve, reject) => {} */ function Promise(excutor) { const self = this // 状态, 初始化值为pending, 成功后为fulfilled, 失败后为rejected self.status = PENDING // 结果数据(成功的或失败的), 初始化没值 self.data = undefined // 用于保存成功回调和失败回调的容器 self.callbacks = [] // 数组中的元素都是: {onResolved () {}, onRejected () {}} /* 异步任务成功后调用的函数 变为成功的状态, 并指定成功的value */ function resolve (value) { // 如果当前不是pending状态, 直接结束 if (self.status!==PENDING) return // 变为成功的状态 self.status = FULFILLED // 保存成功的value self.data = value // 异步执行所有等处理的所有成功回调 if (self.callbacks.length>0) { setTimeout(() => { self.callbacks.forEach(obj => { obj.onResolved(value) }) }, 0) } } /* 异步任务失败后调用的函数 变为失败的状态, 并指定失败的reason */ function reject (reason) { // 如果当前不是pending状态, 直接结束 if (self.status!==PENDING) return // 变为失败的状态 self.status = REJECTED // 保存失败的reason self.data = reason // 异步执行所有等处理的所有失败回调 if (self.callbacks.length>0) { setTimeout(() => { self.callbacks.forEach(obj => { obj.onRejected(reason) }) }, 0) } } // 立即调用excutor excutor(resolve, reject) } /* 用于指定成功和失败回调 方法的返回值是一个新的promise */ Promise.prototype.then = function (onResolved, onRejected) { // 将onResolved, onRejected保存promise对象中 this.callbacks.push({onResolved, onRejected}) }
-
测试
<script src="./Promise.js"></script> <script> const p = new Promise((resolve, reject) => { setTimeout(() => { resolve(2) }, 1000) }) console.log(p) p.then( value => { console.log('onResolved', value) }, reason => { console.log('onRejected', reason) } ) p.then( value => { console.log('onResolved2', value) }, reason => { console.log('onRejected2', reason) } ) </script>
3. 实现then和catch方法
-
Promise.js
Promise.prototype.then = function (onResolved, onRejected) { const self = this // 如果onResolved/onRejected不是函数, 可它指定一个默认的函数 onResolved = typeof onResolved==='function' ? onResolved : value => value // 指定返回的promise为一个成功状态, 结果值为 value onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason} // 指定返回的promise为一个失败状态, 结果值为reason // 返回一个新的promise对象 return new Promise((resolve, reject) => { /* 专门抽取的用来处理promise成功/失败结果的函数 callback: 成功/失败的回调函数 data: 成功/失败的结果数据 */ function handle(callback, data) { // 1. 抛出异常 ===> 返回的promise变为rejected try { const x = callback(data) // 2. 返回一个新的promise ===> 得到新的promise的结果值作为返回的promise的结果值 if (x instanceof Promise) { x.then(resolve, reject) // 一旦x成功了, resolve(value), 一旦x失败了: reject(reason) } else { // 3. 返回一个一般值(undefined) ===> 将这个值作为返回的promise的成功值 resolve(x) } } catch (error) { reject(error) } } if (self.status === FULFILLED) { // 当前promise已经成功了 setTimeout(() => { handle(onResolved, self.data) }) } else if (self.status === REJECTED) { // 当前promise已经失败了 setTimeout(() => { handle(onRejected, self.data) }) } else { // 当前promise还未确定 pending // 将onResolved和onRejected保存起来 self.callbacks.push({ onResolved(value) { handle(onResolved, value) }, onRejected(reason) { handle(onRejected, reason) } }) } }) } /* 用于指定失败回调 方法的返回值是一个新的promise 是then(null, onRejected)的语法糖 */ Promise.prototype.catch = function (onRejected) { return this.then(null, onRejected) }
-
测试
<script src="./Promise.js"></script> <script> new Promise((resolve, reject) => { // setTimeout(() => { // resolve(2) // }, 1000) resolve(2) }).then( value => { console.log('onResolved', value) // - 如果抛出了错误 ==》 失败,reason为抛出的错误 // throw new Error('失败') // throw 4 // - 如果返回失败的promise ==> 失败, reason为返回的失败promise的reason // return new Promise((resolve, reject) => { // reject(5) // }) // - 如果返回成功的promise ==> 成功, value为返回的成功promise的value // return new Promise((resolve, reject) => { // resolve(6) // }) // - 其它所有情况 ==》 成功,value为函数返回的结果 // return undefined // return 7 }, reason => { console.log('onRejected', reason) } ).then( value => { console.log('onResolved2', value) }, reason => { console.log('onRejected2', reason) } ).catch(reason => { console.log('onRejected3', reason) }).then( value => { console.log('onResolved4', value) }, reason => { console.log('onRejected4', reason) } ) </script>
4. 实现静态方法resolve和reject
-
Promise.js
/* 返回一个指定了成功value的promise value: 一般数据或promise */ Promise.resolve = function (value) { return new Promise((resolve, reject) => { if (value instanceof Promise) { // 如果传入的是promise对象, 将此promise的结果值作为返回promise的结果值 value.then(resolve, reject) } else { // 将value作为返回promise的成功结果值 resolve(value) } }) } /* 返回一个指定了失败reason的promise reason: 一般数据/error */ Promise.reject = function (reason) { return new Promise((resolve, reject) => { // 将传入的参数作为返回promise的失败结果值 reject(reason) }) }
-
测试
<script src="./Promise.js"></script> <script> const p1 = Promise.resolve(3) const p2 = Promise.resolve(Promise.resolve(5)) const p3 = Promise.reject(6) p1.then(value => console.log(value)) p2.then(value => console.log(value)) p3.catch(reason => console.log(reason)) </script>
5. 实现静态方法all (可选)
-
Promise.js
/* 返回一个promise, promises所有promise都成功了, 才最终成功 只要promises中有一个失败了, 就直接失败 */ Promise.all = function (promises) { // 返回一个新的promise return new Promise((resolve, reject) => { // 已成功的数量 let resolvedCount = 0 // 待处理的promises数组的长度 const promisesLength = promises.length // 准备一个保存成功值的数组 const values = new Array(promisesLength) // 遍历每个待处理的promise for (let i = 0; i < promisesLength; i++) { // promises中元素可能不是一个数组, 需要用resolve包装一下 Promise.resolve(promises[i]).then( value => { // 成功当前promise成功的值到对应的下标 values[i] = value // 成功的数量加1 resolvedCount++ // 一旦全部成功 if(resolvedCount===promisesLength) { // 将所有成功值的数组作为返回promise对象的成功结果值 resolve(values) } }, reason => { // 一旦有一个promise产生了失败结果值, 将其作为返回promise对象的失败结果值 reject(reason) } ) } }) }
-
测试
<script src="./Promise.js"></script> <script> const p1 = Promise.resolve(3) const p2 = Promise.resolve(Promise.resolve(5)) const p3 = Promise.reject(6) Promise.all([p1, 'abc', p3, p2]).then( values => { console.log('all onResolved()', values) }, reason => { console.log('all onRejected()', reason) } ) Promise.all([p1, 'abc', p2]).then( values => { console.log('all onResolved2()', values) }, reason => { console.log('all onRejected2()', reason) } ) </script>
下一篇:Git学习