目录
题目
这期只有一道题,我们来详细讲讲JavaScript的异步编程,当然,异步编程是许多编程语言都有的一种编程思想,我们在前端这个领域,专注学清楚JavaScript和TypeScript就行。
单选题
6.考虑以下关于 JavaScript 异步编程的说法,哪个是正确的()
A. Promise 对象是一种代表异步操作最终完成或者失败的对象。
B. setTimeout 函数保证在设定的时间后立即执行回调函数。
C. async/await 可以用在普通函数中,不需要函数被声明为异步函数。
D. 在 JavaScript 中,Ajax操作都是非阻塞的。
题解
JavaScript的异步编程
要讲就细致一些讲吧,异步,有编程基础的会知道,我们可以不必等待一段代码执行完之后,再去执行下面的代码,如果在等待其他操作完成的同时,即使运行其他代码,系统也能保持稳定,那么我们务实一点来说,是可以选择异步编程这种思想的。JavaScript在设计之初,是一门单线程的语言,但是我们可以通过一些手段,来完成异步编程
Promise
JavaScript中的异步编程,绕不开Promise,国内可以翻译为"期约",但我觉得我们还是就称为Promise好一些。Promise是ECMAScript6新增的,Promise本身是一种类型,即引用类型,本质是个对象,可以通过new操作符来实例化,比如下面的代码。
javascript
Let p = new Promise( () => {})
setTimeout(console.log, 0, p)
我在读《JavaScript高级程序设计》这本书的时候,书中向读者介绍了一种思想,就是我们可以把Promise看成一个状态机,书中直接就称其为期约状态机,主要有三种状态:待定(pending),兑现(fulfilled或resolved)和拒绝(rejected),Promise只可能是这三种状态,而且特别要注意的是:无论落定哪种状态都是不可逆的。同时,Promise的状态是私有的,JavaScript不能检测到其是哪种状态。
Promise既然是对象,那么它肯定有对应的方法,如前所述,我们说的待定状态有可能转换为兑现和拒绝这两种状态,控制Promise状态状态的转换是通过调用它的两个函数参数实现的,分别是resolve()和reject(),比如我们写下这样的代码,在浏览器中就会有这样的输出。
javascript
let p1 = new Promise((resolve,reject) => resolve())
setTimeout(console.log,0,p1)
let p2 = new Promise((resolve,reject) => reject())
setTimeout(console.log,0,p2)
但回到这期的主题,异步编程,其实这部分代码并不是异步操作,我们来一个实际的例子,来说明Promise是如何完成异步操作的。
javascript
function performAsyncTask() {
return new Promise((resolve, reject) => {
//我们使用setTimeout模拟异步操作,在2秒后执行
setTimeout(() => {
// 随机决定成功或失败
const isSuccess = Math.random() > 0.5
if (isSuccess) {
// 操作成功
resolve("成功")
} else {
// 操作失败
reject("失败")
}
}, 2000)
});
}
// 调用 performAsyncTask 并处理 Promise
performAsyncTask()
.then(successMessage => {
console.log(successMessage)
// 成功处理逻辑
})
.catch(errorMessage => {
console.error(errorMessage)
// 失败处理逻辑
})
我们通过定义一个函数performAsyncTask,这个函数中我们返回一个Promise对象实例,用setTimeout模拟异步操作,真正执行了异步编程的是调用performAsyncTask这一部分。Promise这个请求会在未来的某个时刻返回数据,要么成功,要么失败。
或许会有读者会提出这样的问题,我们声明一个自定义的对象,似乎也能完成这个操作?理论上是可以的,但别忘了,我们使用的resolve和reject方法,都是Promise对象已经有的方法,如果自定义对象,你还要自定义这些功能函数。通过前面的例子,其实选项A自然已经有答案了,Promise对象是一种代表异步操作最终完成或者失败的对象。Promise的优点在于它可以用一种链式结构,把多个异步操作串联起来
同时,这里我们也用到了setTimeout函数,正确的表述是:setTimeout保证的是,在设定的时间后,回调函数被添加到任务队列中,如果任务队列中已经有其他任务在等待,那么回调函数需要等待其他任务执行完毕之后才能执行。
异步函数async/await
async/await是ES8规范新增的,这个异步函数的引入,让程序员可以以同步方式写的代码,能够异步执行。规范来讲,JavaScript的异步编程的方式只有两种:Promise和回调函数,async/await其实是基于Promise的更方便的方式,async/await都是关键字,所以其实这种一种编程方式。所以,使用了async/await关键字的函数,就是异步函数了,不再是普通函数了。
比如这个例子,用async来表示这个函数是一个异步函数,我还是选择用Promise对象作为返回值,这样就表明了我们在使用异步编程,这里我就先简单的例子来。输出结果为3。
javascript
async function f0() {
let a = new Promise((resolve, reject) => setTimeout(resolve, 1000, 3));
console.log(await a);
}
fo()
关于Ajax
完整的Ajax的介绍,我就不详细讲了,后面我会专门出文章,讲一讲网络请求。我们需要记住的是:Ajax中的XMLHttpRequest是有同步和异步两种设置的,如果我们在open方法的参数中,设置为true,即异步的Ajax,那么它是阻塞的;而如果把参数设置为false,Ajax就是同步的,同步的Ajax就是非阻塞的。