Appearance
异步编程
回调函数
的方式,使用回调函数的方式有一个缺点是,多个回调函数嵌套的时候会造成回调函数地狱,上下两层的回调函数间的代码耦合度太高,不利于代码的可维护。Promise
的方式,使用Promise
的方式可以将嵌套的回调函数作为链式调用。但是使用这种方法,有时会造成多个 then 的链式调用,可能会造成代码的语义不够明确。generator
的方式,它可以在函数的执行过程中,将函数的执行权转移出去,在函数外部还可以将执行权转移回来。当遇到异步函数执行的时候,将函数执行权转移出去,当异步函数执行完毕时再将执行权给转移回来。因此在generator
内部对于异步操作的方式,可以以同步的顺序来书写。使用这种方式需要考虑的问题是何时将函数的控制权转移回来,因此需要有一个自动执行generator
的机制,比如说co
模块等方式来实现generator
的自动执行。async
函数 的方式,async
函数是generator
和promise
实现的一个自动执行的语法糖,它内部自带执行器,当函数内部执行到一个await
语句的时候,如果语句返回一个promise
对象,那么函数将会等待promise
对象的状态变为resolve
后再继续向下执行。因此可以将异步逻辑,转化为同步的顺序来书写,并且这个函数可以自动执行。
Promise
Promise 是异步编程的一种解决方案,解决异步编程回调函数造成的回调地狱问题;
三种状态
pending
fulfilled
rejected
状态转换
pending
->fulfilled
pending
->rejected
注:pending 状态一旦被转换就不再更改。
常用方法
then
方法,接收两个回调函数作为参数,第一个fulfilled
时调用,第二个rejected
时调用;catch
方法,相当于then
方法的第二个参数,通常用于兜底捕获异常;finally
方法,不管最后状态如何都会执行;all
方法,当所有promise
状态都为fulfilled
时才为fulfilled
,如果有一个为rejected
状态就为rejected
;race
方法,当首个promise
状态发生改变时,状态与这个promise
一致;
应用场景
- 回调函数改造,支持 promise 写法,例如:XMLHttpRequest;
js
function request(options) {
return Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
xhr.readyState == 4 && xhr.status == 200
? resolve(xhr.responseText)
: reject()
}
xhr.open(options.method, options.url, true)
xhr.send(options.data)
})
}
- 创建竞态条件,以保证 Promise 始终有输出信号,例如:超时操作,未在指定时间内返回;
js
const timeout = new Promise((resolve, reject) =>
setTimeout(reject, 10 * 1000)
)
const fetchList = fetch('xxx.xxx.xxx/xxx/xxx')
Promise.race([fetchList(), timeout()])
.then(() => {
console.log('请求成功')
})
.catch(() => {
console.log('请求失败或超时')
})
generator
生成器函数,可手动控制执行进度,并且其执行结果是一个迭代器,即可使用 for of
遍历。
js
function* gen() {
yield 1
yield 1 + 1
return 3
}
const g = gen()
g.next() // { value: 1, done: false }
g.next() // { value: 2, done: false }
g.next() // { value: 3, done: true }
for (const value of gen()) {
console.log(value)
}
// 1
// 2
next
可传一个参数,该参数作为上一个 yield
表达式的值赋给变量。
js
function* gen(a) {
const b = yield a + 1
const c = yield b + 2
return a + b + c
}
const g = gen(10)
g.next() // { value: 11, done: false }
g.next(-5) // { value: -3, done: false } b = -5
g.next(-1) // { value: 4, done: false } c = -1
异常处理,throw
方法可抛出异常并提前结束。
js
function* gen() {
try {
yield 1
yield 2
return
} catch (error) {
console.error(error) // 出错咯
}
}
const g = gen()
g.next() // { value: 1, done: false }
g.throw('出错咯!')
g.next() // { value: undefined, done: true }
return
方法可提前结束;
js
function* gen() {
yield 1
yield 2
return 3
}
const g = gen()
g.next() // { value: 1, done: false }
g.return() // { value: undefined, done: true }
g.next() // { value: undefined, done: true }
应用场景
- 使用迭代器遍历链表。
js
const d = { value: 'd', next: null }
const c = { value: 'c', next: d }
const b = { value: 'b', next: c }
const a = { value: 'a', next: b }
const header = a
function* iterator(link) {
while (link) {
yield link.value
link = link.next
}
}
for (const value of iterator(header)) {
console.log(value)
}
// a
// b
// c
// d
async/await
async/await
其实是 Generator
的语法糖,自带执行器的 Generator
,并且返回 Promise
。解决了 Promise
顺序执行 then
方法链式调用过长问题。
js
function* gen() {
let result
result = yield 1
console.log(result) // 1
result = yield 2
console.log(result) // 2
result = yield 3
console.log(result) // 3
result = yield 4
console.log(result) // 4
result = yield 5
console.log(result) // 5
result = yield 6
console.log(result) // 6
return 7
}
function runGenerator(generatorFunction, ...args) {
const generator = generatorFunction(...args)
const next = (...args) => {
const nextValue = generator.next(...args)
if (nextValue.done) return Promise.resolve(nextValue.value)
return Promise.resolve(nextValue.value).then(next)
}
return next()
}
runGenerator(gen).then(res => {
console.log(res) // 7
})