一、JavaScript异步编程
1.同步模式
console.log('global begin')
function bar () {
console.log('bar task')
}
function foo () {
console.log('foo task')
bar()
}
foo()
console.log('global end')
// 执行引擎会把所有的代码在调用栈(类似工作表)中压入一个匿名函数全局调用, 然后逐行执行,将第一行压入调用栈,控制台打印global begin,调用结束弹出调用栈 往后循环,
2.异步模式
console.log('global begin')
setTimeout(function timer1 () {
console.log('timer1 invoke')
}, 1800)
setTimeout(function timer2 () {
console.log('timer2 invoke')
setTimeout(function inner () {
console.log('inner invoke')
}, 1000)
}, 1000)
console.log('global end')
以上调用顺序
1.将所有代码在调用栈中压入一个匿名函数全局调用,执行第一行,因为是同步代码,直接执行 打印出global begin
2.遇到setTimeout异步代码,将timer1 放入到API环境中等待执行,1.8s后
3.遇到setTimeout异步代码,将timer2 放入到API环境中等待执行 1s后
4.遇到console.log同步代码,直接执行 打印出 global end
5. timer2等待时间短,时间结束后timer2放入到消息队列(queue)中的第一位,然后timer1放入到消息队列
6.一旦调用栈执行完,事件循环(event loop)就会监听到然后将消息队列中的第一个任务压入调用栈执行,打印出timer2 invoke
然后再打印出timer1 invoke
7.在timer2中执行的时候,遇到异步代码,将inner放入到API环境中等待执行,timer1执行完后,inner进入消息队列,打印inner invoke
二、回调函数(异步编程函数的基础)
promise 三种状态,pending,Fulfilled(成功) ,Rejected (失败) 成功或失败一旦确定无法修改。
// Promise 基本示例
const promise = new Promise(function (resolve, reject) {
// 这里用于“兑现”承诺
// resolve(100) // 承诺达成
reject(new Error('promise rejected')) // 承诺失败
})
promise.then(function (value) {
// 即便没有异步操作,then 方法中传入的回调仍然会被放入队列,等待下一轮执行
console.log('resolved', value)
}, function (error) {
console.log('rejected', error)
})
console.log('end')
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/foo.json').then(function (res) {
console.log(res)
}, function (error) {
console.log(error)
})
Promise链式调用(then方法返回的也是一个链式调用,全新的promise对象,和原promise不等)
后面的then方法就是在为上一个then返回的promise注册回调
前面then方法中回调函数的返回值会作为后面then方法回调的参数
如果回调中返回的是Promise,那后面的then方法的回调会等待他的结束
ajax('/api/users.json')
.then(function onFulfilled (value) {
console.log('onFulfilled', value)
return ajax('/error-url')
}, function onRejected (error) { //这种写法是获取当前promise的异常
console.log('onRejected', error)
})
ajax('/api/users.json')
.then(function onFulfilled (value) {
console.log('onFulfilled', value)
return ajax('/error-url')
}) // => Promise {}
// .catch(function onRejected (error) { 获取then方法返回的promise的异常
// console.log('onRejected', error)
// })
Promise.resolve('foo')
.then(function (value) {
console.log(value) // foo
})
new Promise(function (resolve, reject) {
resolve('foo')
})
//如果传入的是一个 Promise 对象,Promise.resolve 方法原样返回
var promise = ajax('/api/users.json')
var promise2 = Promise.resolve(promise)
console.log(promise === promise2) //true
//如果传入的是带有一个跟 Promise 一样的 then 方法的对象,
Promise.resolve //会将这个对象作为 Promise 执行
//原生promise对象
Promise.resolve({
then: function (onFulfilled, onRejected) {
onFulfilled('foo')
}
})
.then(function (value) {
console.log(value)
})
//Promise.reject 传入任何值,都会作为这个 Promise 失败的理由
Promise.reject(new Error('rejected'))
.catch(function (error) {
console.log(error)
})
Promise.reject('anything')
.catch(function (error) {
console.log(error)
})
promise并行执行 promise.all 所有的promise执行完
var promise = Promise.all([
ajax('/api/users.json'),
ajax('/api/posts.json')
])
promise.then(function (values) {
console.log(values)
}).catch(function (error) {
console.log(error)
})
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)
})
promise.race 等待第一个promise结束
// 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)
})
微任务(promise的回调作为微任务是任务执行完后再执行process.nextTick mutationObserver)
console.log('global start')
// setTimeout 的回调是 宏任务,进入回调队列排队
setTimeout(() => {
console.log('setTimeout')
}, 0)
// Promise 的回调是 微任务,本轮调用末尾直接执行
Promise.resolve()
.then(() => {
console.log('promise')
})
.then(() => {
console.log('promise 2')
})
.then(() => {
console.log('promise 3')
})
console.log('global end')
generator异步方案
function * foo () {
console.log('start')
try {
const res = yield 'foo'
console.log(res)
} catch (e) {
console.log(e)
}
}
const generator = foo()
const result = generator.next()
console.log(result) //start
//{ value: 'foo', done: false }
// generator.next('bar')
generator.throw(new Error('Generator error')) //Error: Generator error
function ajax (url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response)
} else {
reject(new Error(xhr.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/urls11.json')
console.log(urls)
} catch (e) {
console.log(e)
}
}
function co (generator) {
const g = generator()
function handleResult (result) {
if (result.done) return // 生成器函数结束
result.value.then(data => {
handleResult(g.next(data))
}, error => {
g.throw(error)
})
}
handleResult(g.next())
}
co(main)
async await
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)
}
}
// co(main)
const promise = main()
promise.then(() => {
console.log('all completed')
})