【JavaScript】异步解决方案发展历程
JavaScript 异步解决方案发展历程及获取异步方法中数据的几种方式
什么是异步?
当前一个任务被执行时,不会等待任务执行完成后就去执行下一个任务,等前一个任务执行完成后,将去执行其返回的回调函数,这就是异步操作,同步为阻塞模式,异步为非阻塞模式;可以理解为与现实生活中相反
JavaScript 中为什么需要异步?
首先我们知道 JavaScript
是单线程的(即使新增了 webworker
,但是本质上 JS 还是单线程)。同步代码意味着什么呢?意味着有可能会阻塞,当我们有一个任务需要时间较长时,如果使用同步方式,那么就会阻塞之后的代码执行。而异步则不会,我们不会等待异步代码的之后,继续执行异步任务之后的代码。
异步解决方案发展历程:
回调函数 callback
被作为实参传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数。如 setTimeOut
,ajax
请求,readFile
等
function greeting(name) {
console.log('Hello,' + name)
}
function processUserInput(callback) {
let name = 'ABing'
callback(name)
}
processUserInput(greeting) // ABing
优点:解决了异步问题
缺点:回调地狱,多个回调函数嵌套的情况,使代码看起来十分混乱,不易于维护
Promise
Promise
是 ES6
提出的异步编程的一种解决方案
Promise
对象有三种状态:
pending
:初始状态,既不是成功,也不是失败fulfilled
:操作成功完成rejected
:操作失败
promise
的状态只能从 pending
变成 fulfilled
,和 pending
变成 rejected
,状态一旦改变,就不会再改变,且只有异步操作的结果才能改变 promise
的状态
function person(name) {
return new Promise((resolve, reject) => {
resolve(name)
})
}
person('ABing').then(data => console.log(data)) // ABing
优点:解决了回调地狱的问题,将异步操作以同步操作的流程表达出来
缺点:无法取消 Promise
;如果不设置回调函数,Promise
内部抛出一个错误,不会反映到外部。当处于 Pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。当执行多个 Promise
时,一堆 then
看起来也很不友好
Generator
Generator
是 es6
提出的另一种异步编程解决方案,需要在函数名之前加一个*
号,函数内部使用 yield
语句。Generaotr
函数会返回一个遍历器,可以进行遍历操作执行每个中断点 yield
function* count() {
yield 1
yield 2
return 3
}
let c = count()
console.log(c.next()) // { value: 1, done: false }
console.log(c.next()) // { value: 2, done: false }
console.log(c.next()) // { value: 3, done: true }
console.log(c.next()) // { value: undefined, done: true }
优点:没有了 Promise
的一堆 then()
,异步操作更像同步操作,代码更加清晰
缺点:不能自动执行异步操作,需要写多个 next()
方法,需要配合使用 Thunk
函数和 Co
模块才能做到自动执行
async/await
async
是 es2017 引入的异步操作解决方案,可以理解为 Generator
的语法糖,async
等同于 Generator
和 co
模块的封装,async
函数返回一个 Promise
async function Data() {
return 'ABing'
}
async function getData() {
console.log(await Data())
}
getData() // ABing
优点:内置执行器,比 Generator
操作更简单。async/await
比*/yield
语义更清晰。返回值是 Promise
对象,可以用 then
指定下一步操作。代码更整洁。可以捕获同步和异步的错误
缺点:频繁使用可能会导致网页阻塞,一致推荐的异步操作写法
获取异步方法中的数据的几种方式:
使用回调函数
function Data(callback) {
setTimeout(function () {
let name = 'ABing'
callback(name)
}, 1000)
}
Data(function (name) {
console.log(name) // ABing
})
使用 Promise
let p = new Promise(function (resolve, reject) {
setTimeout(function () {
let name = 'ABing'
resolve(name)
}, 1000)
})
p.then(data => console.log(data)) // ABing
或者
function Data(resolve, reject) {
setTimeout(function () {
let name = 'ABing'
resolve(name)
}, 1000)
}
let p = new Promise(Data)
p.then(data => console.log(data))
使用 async 和 await
async function Data() {
return 'ABing'
}
async function getData() {
console.log(await Data())
}
getData() // ABing
总结
JS
的异步发展史,可以认为是从 callback
-> promise
-> generator
-> async/await
。async/await
使得异步代码看起来像同步代码,异步编程发展的目标就是让异步逻辑的代码看起来像同步一样