async

why async
传统callback方式
asyncCallone(() => {
asyncCallTwo(() => {
asyncCallThree(() => {
...
})
})
})Promise
解决问题
多层嵌套的问题
每种任务的处理结果存在两种可能性(fulfilled or rejected, default is pending),那么需要在每种任务执行结束后分别处理这两种可能性
如何解决
回调函数延迟绑定
返回值穿透
错误冒泡
缺点
无法取消Promise,一旦新建它就会立即执行,无法中途取消
如果不设置回调函数,promise内部抛出的错误,不会反应到外部
当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
API
Promise.resolve若参数是
Promise实例,那么Promise.resolve将不做任何修改,原封不动地返回这个实例若参数是
thenable对象,Promise.resolve方法会将这个对象转为Promise对象,然后就立即执行thenable对象的then方法若参数是一个原始值,或是一个不具有
then方法的对象,则Promise.resolve方法返回一个新的Promise对象,状态为resolved若不带有任何参数,直接返回一个
resolved状态的Promise对象
Promise.reject(其参数会原封不动地作为reject的理由,变成后续方法的参数,这一点与Promise.resolve方法不一致)Promise.all(其中任意一个promise被reject,Promise.all就会立即被reject,数组中其它未执行完的promise依然在执行,Promise.all没有任何措施可以取消它们的执行)适合场景:彼此相互依赖,其中任何一个被
reject,其它都失去了价值
Promise.allSettled适合场景:彼此互不依赖,其中任何一个被
reject,对其它都没有影响适合场景:期望知道每个
promise的执行结果
Promise.any
Promise.prototype.then
Promise.prototype.finally
由于无法知道
promise的最终状态,所以finally的回调函数中不接收任何参数,仅用于无论最终结果如何都要执行的情况与
Promise.resolve(2).then(() => {}, () => {})(resolved的结果为undefined)不同,Promise.resolve(2).finally(() => {})resolved的结果为2,同样的Promise.reject(3).finally(() => {})rejected 的结果为3
Knowledge Point
三种状态:
pending、fulfilled和rejected初始状态是
pending, 执行了resolve,Promise状态会变成fulfilled; 执行了reject,Promise状态会变成rejectedPromise状态不受外界影响Promise只以第一次决议为准,第一次成功就永久为
fulfilled,第一次失败就永远状态为rejectedPromise中有
throw的话,就相当于执行了rejectPromise里没有执行
resolve、reject以及throw的话,这个promise的状态也是pending基于上一条,
pending状态下的promise不会执行then中的回调函数必须给
Promise对象传入一个执行函数,否则报错
Generator
function* gen() {
let a = yield 111;
console.log(a);
let b = yield 222;
console.log(b);
let c = yield 333;
console.log(c);
let d = yield 444;
console.log(d);
}
let t = gen();
t.next(1); //第一次调用next函数时,传递的参数无效,故无打印结果
t.next(2); // 2
t.next(3); // 3
t.next(4); // 4
t.next(5); // 5A/A
语法简洁,更像是同步代码,也更符合普通的阅读习惯
改进JS中异步操作串行执行的代码组织方式,减少callback的嵌套
Promise中不能自定义使用try/catch进行错误捕获,但是在Async/await中可以像处理同步代码处理错误
async返回值

const testA = async () => 1
testA().then(() => console.log(1))
Promise.resolve()
.then(() => console.log(2))
.then(() => console.log(3))const testB = async () => {
return {
then(cb) {
cb()
},
}
}
testB().then(() => console.log(1))
Promise.resolve()
.then(() => console.log(2))
.then(() => console.log(3))
const testC = async () => {
return new Promise((resolve, reject) => {
resolve()
})
}
testC().then(() => console.log(1))
Promise.resolve()
.then(() => console.log(2))
.then(() => console.log(3))
.then(() => console.log(4))
await 右值类型区别

await后面接非thenable类型,会立即向微任务队列添加一个微任务then,但不需等待
非 thenable
async function test() {
console.log(1)
await 1
console.log(2)
}
test()
console.log(3)function func() {
console.log(2)
}
async function test() {
console.log(1)
await func()
console.log(3)
}
test()
console.log(4)async function test() {
console.log(1)
await 123
console.log(2)
}
test()
console.log(3)
Promise.resolve()
.then(() => console.log(4))
.then(() => console.log(5))
.then(() => console.log(6))
.then(() => console.log(7))thenable类型
await后面接thenable类型,需要等待一个then的时间之后执行
async function test() {
console.log(1)
await {
then(cb) {
cb()
},
}
console.log(2)
}
test()
console.log(3)
Promise.resolve()
.then(() => console.log(4))
.then(() => console.log(5))
.then(() => console.log(6))
.then(() => console.log(7))==Promise类型==
async function test() {
console.log(1)
await new Promise((resolve, reject) => {
resolve()
})
console.log(2)
}
test()
console.log(3)
Promise.resolve()
.then(() => console.log(4))
.then(() => console.log(5))
.then(() => console.log(6))
.then(() => console.log(7))
// 1 3 2 4 5 6 7async function func() {
console.log(1)
await 1
console.log(2)
await 2
console.log(3)
await 3
console.log(4)
}
async function test() {
console.log(5)
await func()
console.log(6)
}
test()
console.log(7)
Promise.resolve()
.then(() => console.log(8))
.then(() => console.log(9))
.then(() => console.log(10))
.then(() => console.log(11))
//5 1 7 2 8 3 9 4 10 6 11await一定要等到右侧的表达式有确切的值才会放行,否则将一直等待
function func() {
return new Promise((resolve) => {
console.log('B')
// resolve()
})
}
async function test() {
console.log(1)
await func()
console.log(3)
}
test()
console.log(4)
// 1 B 4 (永远不会打印3)
//or
async function test() {
console.log(1)
await new Promise((resolve) => {
console.log('B')
// resolve()
})
console.log(3)
}
test()
console.log(4)
// 1 B 4 (永远不会打印3)demo
async getBookByAuthor(authorId) {
const books = await bookModel.fetchAll()
return books.filter(b => b.authorId === authorId)
}
//promise way
getBooksByAuthor2(authorId) {
return bookModel.fetchAll().then(books => books.filter(b => b.authorId === authorId))
}let p1 = Promise.reject(100)
async function fn1() {
let result = await p1
console.log(1) //dead code
}Practices
并行调用
//wrong
async getBooksByAuthor(authorId) {
const books = await bookModel.fetchAll()
const author = await authorModel.fetch(author)
return {
author,
books: books.filter(book => book.authorId === authorId)
}
}
//correct
async getBooksByAuthor(authorId) {
const bookPromise = bookModel.fetchAll()
const authorPromise = authorModel.fetch(author)
const book = await bookPromise
const author = await authorPromise
return {
author,
books: books.filter(book => book.authorId === authorId)
}
}
async getAuthors(authorIds) {
//wrong
const authors = _.map(
authorIds,
id => await authorModel.fetch(id)
)
//correct
const promises = _.map(authorIds, id => authorModel.fetch(id))
const authors = await Promise.all(promises)
}让Promise.all正常执行完成即使出现异常
function getBannerList(){
return new Promise((resolve,reject)=>{
setTimeout(function(){
// 假设这里 reject 一个异常
reject(new Error('error'))
},300)
})
}
function getStoreList(){
// ...
}
function getCategoryList(){
// ...
}
function initLoad(){
Promise.all([
getBannerList().catch(err=>err),
getStoreList().catch(err=>err),
getCategoryList().catch(err=>err)
]).then(res=>{
if(res[0] instanceOf Error){
// 处理异常
} else {
// 渲染数据
}
if(res[1] instanceOf Error){
// 处理异常
} else {
// 渲染数据
}
if(res[2] instanceOf Error){
// 处理异常
} else {
// 渲染数据
}
})
}
initLoad()race使用场景
使用
Promise.race把异步操作和定时器放到一起,若定时器先触发,认为超时,告知用户图片等资源有多个存放路径,但是不确定哪个路径的资源更快,可以用该方法同时请求多个路径,哪个路径的资源最先拿到,使用哪个资源
function requestImg(path){
return new Promise(function(resolve, reject){
let img = new Image()
img.onload = resolve
img.onerror = reject
img.src = path
})
}
// 定时功能的延迟函数
const timeout = (delay = 3000) => {
return new Promise(function(resolve, reject){
setTimeout(function(){
reject('Picture request timeout')
}, delay)
})
}
Promise
.race([requestImg(), timeout(3000)])
.then(function(results){
// 该资源请求在指定时间内完成
console.log(results)
})
.catch(function(reason){
// 该资源请求被在指定时间内没有完成
console.log(reason)
});allSettled使用场景
const promises = [
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
];
const results = await Promise.allSettled(promises)
// 过滤出成功的请求
const successfulPromises = results.filter(p => p.status === 'fulfilled');
// 过滤出失败的请求,并输出原因
const errors = results
.filter(p => p.status === 'rejected')
.map(p => p.reason);
removeLoadingIndicator();// 移除加载的滚动图标红黄绿灯
const red = () => console.log('red')
const yellow = () => console.log('yellow')
const green = () => console.log('green')
const light = (cb, timeout) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
cb()
resolve()
}, timeout)
})
}
let endCnt = 0
const start = () => {
if (endCnt++ >= 3) {
console.log('finish lighting~~')
return
}
Promise.resolve()
.then(() => {
return light(red, 3000)
})
.then(() => {
return light(yellow, 2000)
})
.then(() => {
return light(green, 1000)
})
.then(() => {
start()
})
}
start()异步执行函数
const repeat = (cb, times, delay = 1000) => {
return async function (...args) {
for (let i = 0; i < times; i++) {
await new Promise((resolve, reject) => {
setTimeout(() => {
cb.call(null, ...args)
resolve()
}, delay)
})
}
}
}
const repeatFn = repeat(console.log, 4, 1000)
repeatFn('hello')generator实现
function* repeatedArr(arr) {
let i = 0
while (true) {
yield arr[i++ % arr.length]
}
}
let infiniteNameList = repeatedArr(starks)
let sleep = (ms) =>
new Promise((resolve) => {
setTimeout(() => {
resolve()
}, ms)
})
;(async () => {
for (const name of infiniteNameList) {
await sleep(1000)
console.log(name)
}
})()PromiseQueue
class PromiseQueue {
constructor(tasks, concurrentCount = 1) {
this.totals = tasks.length
this.todo = tasks
this.count = concurrentCount
this.running = []
this.complete = []
}
runNext() {
return this.running.length < this.count && this.todo.length
}
run() {
while (this.runNext()) {
let promise = this.todo.shift()
promise.then(() => {
this.complete.push(this.running.shift())
this.run()
})
this.running.push(promise)
}
}
}Last updated