大前端学习--异步编程
异步编程
一、JS是单线程语言
JS执行环境找那个负责执行代码的线程只有一个
执行任务的模式有两种:同步模式、异步模式。
二、Promise
1. 基本使用
// Promise 基本演示
const promise = new Promise(function (resolve, reject) {
// 这里用于兑现承诺
// resolve(100) // 承诺达成
reject(new Error('promise rejected')) // 承诺失败
})
promise.then(function (value) {
console.log('resolved', value)
}, function (error) {
console.log('rejected', error)
})
console.log('end') // 先打印出end,再打印Error
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
2. 通过Promise封装ajax
function ajax(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest()
xhr.open("GET", url)
xhr.responseType = 'json'
xhr.onload = function () {
if(this.status === 200)
resolve(this.response)
else {
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
ajax('/api/users.json2').then(function (res) {
console.log(res)
}, function (error) {
console.log(error)
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
3. Promise通过链式调用避免回调嵌套
- Promise对象的then方法会返回一个全新的Promise对象
- 后面的then方法就是在为上一个then返回的Promise注册回调
- 前面then方法中回调函数的返回值会作为后面then方法回调的参数
- 如果回调返回的是Promise,那后面then方法的回调会等待它的结束
function ajax(url) {
return new Promise(function(resolve, reject) {
// foo()
var xhr = new XMLHttpRequest()
xhr.open("GET", url)
xhr.responseType = 'json'
xhr.onload = function () {
if(this.status === 200)
resolve(this.response)
else {
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
var promise = ajax('/api/users.json')
// then 方法返回一个全新的promise对象
var promise2 = promise.then(function (res) {
console.log(res)
}, function (error) {
console.log(error)
})
console.log(promise2 === promise) // false
// 每一个then方法都是在为上一个then方法添加状态明确过后的回调
ajax('/api/users.json')
.then(function (value) {
console.log(111)
return ajax('/api/users.json')
}) // => Promise
.then(function (value) {
console.log('yi', value)
console.log(222)
return 'foo'
}) // => Promise
.then(function (value) {
console.log(333)
console.log('jal', value)
}) // => Promise
.then(function (value) {
console.log(444)
console.log('ji', value)
}).catch(function onRejected(error) {
console.log('onRejected', error)
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
其中catch也是then的别名
.catch(function onRejected(error) {
console.log('onRejected', error)
})
// 就相当于
.then(undefined, function (value) {
console.log(444)
console.log('ji', value)
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
then中的第二个参数是reject函数,catch中的参数也是reject函数,但是作用不太相同,then中的reject不能捕获到第一个参数中的resolve中的异常,但是catch由于链式作用,能捕获到前面任意处的异常
ajax('/api/users.json')
.then(function (value) {
console.log(111)
return ajax('/api/users2.json') // 这个异常无法捕获
}, function onRejected (e){
console.log('reject', e)
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
推荐使用catch捕获异常,可以捕获整个promise链条上的异常:
ajax('/api/users.json')
.then(function (value) {
console.log(111)
return ajax('/api/users2.json')
}).catch(function onRejected (e){
console.log('reject', e)
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
还可以全局捕获异常:
浏览器中,在window对象上注册事件:
window.addEventListener('unhandledrejection', event => {
const {reason, promise} = event
console.log(reason, promise)
// reason => Promise 失败原因,一般是一个错误对象
// promise => 出现异常的Promise对象
event.preventDefault()
}, false)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
node中:
process.on('unhandledRejection', (reason, promise) => {
const {reason, promise} = event
console.log(reason, promise)
// reason => Promise 失败原因,一般是一个错误对象
// promise => 出现异常的Promise对象
})
- 1
- 2
- 3
- 4
- 5
- 6
尽量在代码中明确捕获每一个可能的异常,而不是丢给全局处理。
5. Promise静态方法
-
Promise.resolve() 快速的把一个值转化为Promise对象
Promise.resolve('foo') .then(function (value) { console.log(value) // 'foo' }) // Promise.resolve('foo') 等价于 new Promise(function (resolve, reject){ resolve('foo') })
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
var promise = ajax('/api/users.json') var promise2 = Promise.resolve(promise) console.log(promise === promise2) // true
- 1
- 2
- 3
// 带有then方法的对象,就是实现了thenable接口, 可以被then的对象 Promise.resolve({ then: function (onFulfilled, onRejected) { onFulfilled('foo') } }).then(function (value){ console.log(value) // foo })
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
-
Promise.reject(err) 传入的对象为失败的原因
Promise.reject('anything') .catch(function (err) { console.log(err) // anything })
- 1
- 2
- 3
- 4
6. Promise并行执行
-
Promise.all() 等待所有任务成功结束了,才算结束
var promise = Promise.all([ ajax('/api/users.json'), ajax('/api/posts.json'), ]) // 只有promise里面的每一个任务都执行成功了才进入resolve // 其中任何一个失败了,都会进入catch promise.then(function (values) { console.log(values) // 返回一个数组 // Array(2) // 0: // username: "yibo" // __proto__: Object // 1: // name: "jiailing" }).catch(function(err){ console.log(err) }) ajax('/api/urls.json') .then( value => { const urls = Object.values(value) const tasks = urls.map(url => ajax(url)) return Promise.all(tasks) }) .then(values => { console.log(values) })
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
-
Promise.race() 只会等待第一个结束的任务
const request = ajax('/api/posts.json') const timeout = new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('timeout')) }, 500); }) Promise.race([ request, timeout ]) .then(value=>{ console.log(value) }) .catch(error=>{ console.log(error) }) // 实现ajax请求超时控制的一种方式
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
7. 微任务
即使Promise中没有任何异步操作,它的回调函数仍然会进入到回调队列中排队。必须等待所有同步代码执行完毕后,Promise中的代码才会被调用。
JS回调队列中的任务称之为【宏任务】,而宏任务执行过程中可以临时加上一些额外需求,可以选择作为一个新的宏任务进到队列中排队(如setTimeout),也可以作为当前任务的【微任务】,直接在当前任务结束后立即执行。
Promise的回调会作为微任务执行。微任务的目的是为了提高整体的响应能力,目前绝大多数异步调用都是作为宏任务执行,Promise 、MutationObserver、process.nextTick 是作为微任务在本轮调用的末尾执行。
console.log('global start') // 第一个打印
Promise.resolve()
.then(()=>{
console.log('promise')// 第3个打印
})
.then(()=>{
console.log('promise 2')// 第4个打印
})
.then(()=>{
console.log('promise 3')// 第5个打印
})
console.log('global end')// 第2个打印
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
setTimeout属于宏任务
console.log('global start') // 第一个打印
setTimeout(() => {
console.log('last') // 最后调用
}, 0);
Promise.resolve()
.then(()=>{
console.log('promise')// 第3个打印
})
.then(()=>{
console.log('promise 2')// 第4个打印
})
.then(()=>{
console.log('promise 3')// 第5个打印
})
console.log('global end')// 第2个打印
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
三、Generator异步方案
1. Generator的基本使用
生成器函数会返回一个生成器对象,调用这个生成器对象的next方法,才会让函数体执行,一旦遇到了yield关键词,函数的执行则会暂停下来,yield后面的值作为next函数的结果返回,如果继续调用函数的next函数,则会再上一次暂停的位置继续执行,知道函数体执行完毕,next返回的对象的done就变成了true
function * fn () {
console.log(111)
yield 100
console.log(222)
yield 200
console.log(333)
yield 300
}
const generator = fn()
console.log(generator.next())
// 111
// { value: 100, done: false }
console.log(generator.next())
// 222
// { value: 200, done: false }
console.log(generator.next())
// 333
// { value: 300, done: false }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
2. Generator实现异步
注意:generator.next(value)中,next传入的参数会作为上一次yield的返回值。
function ajax(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest()
xhr.open("GET", url)
xhr.responseType = 'json'
xhr.onload = function () {
if(this.status === 200)
resolve(this.response)
else {
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
// 生成器函数
function * main () {
const users = yield ajax('/api/users.json')
console.log(users)
const posts = yield ajax('/api/posts.json')
console.log(posts)
const urls = yield ajax('/api/urls.json')
console.log(urls)
}
// 调用生成器函数得到一个生成器对象
const generator = main()
// 递归实现generator.next()的调用,直到done为true终止
function dfs(value) {
const result = generator.next(value)
if(result.done) return
result.value.then(data=>{
console.log(data)
dfs(data)
})
}
dfs()
// 打印结果
// Generator实现异步.js:35 {username: "yibo"}
// Generator实现异步.js:19 {username: "yibo"}
// Generator实现异步.js:35 {posts: "jiailing"}
// Generator实现异步.js:22 {posts: "jiailing"}
// Generator实现异步.js:35 {posts: "/api/posts.json", users: "/api/users.json"}
// Generator实现异步.js:25 {posts: "/api/posts.json", users: "/api/users.json"}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
封装生成器函数执行器co
function ajax(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest()
xhr.open("GET", url)
xhr.responseType = 'json'
xhr.onload = function () {
if(this.status === 200)
resolve(this.response)
else {
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
// 生成器函数
function * main () {
try {
const users = yield ajax('/api/users.json')
console.log(users)
const posts = yield ajax('/api/posts.json')
console.log(posts)
const urls = yield ajax('/api/urls.json')
console.log(urls)
} catch(e) {
// 如果生成器函数中,发生了异常,会被生成器对象的throw方法捕获
console.log(e)
}
}
// 封装了一个生成器函数执行器
function co(main) {
// 调用生成器函数得到一个生成器对象
const generator = main()
// 递归实现generator.next()的调用,直到done为true终止
function handleResult(result) {
if(result.done) return
result.value.then(data=>{
console.log(data)
handleResult(generator.next(data))
}, error => {
g.throw(error)
})
}
handleResult(generator.next())
}
co(main)
// Generator实现异步.js:42 {username: "yibo"}
// Generator实现异步.js:20 {username: "yibo"}
// Generator实现异步.js:42 {posts: "jiailing"}
// Generator实现异步.js:23 {posts: "jiailing"}
// Generator实现异步.js:42 {posts: "/api/posts.json", users: "/api/users.json"}
// Generator实现异步.js:26 {posts: "/api/posts.json", users: "/api/users.json"}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
四、Async/Await 语法糖
await关键词只能出现在async函数中。
function ajax(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest()
xhr.open("GET", url)
xhr.responseType = 'json'
xhr.onload = function () {
if(this.status === 200)
resolve(this.response)
else {
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
async function main () {
try {
const users = await ajax('/api/users.json')
console.log(users)
const posts = await ajax('/api/posts.json')
console.log(posts)
const urls = await ajax('/api/urls.json')
console.log(urls)
} catch(e) {
console.log(e)
}
}
main()
// async-await.js:20 {username: "yibo"}
// async-await.js:23 {posts: "jiailing"}
// async-await.js:26 {posts: "/api/posts.json", users: "/api/users.json"}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
文章来源: blog.csdn.net,作者:爱玲姐姐,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/jal517486222/article/details/106212764
- 点赞
- 收藏
- 关注作者
评论(0)