javascript与其他语言的经典不同在于,javascript是异步的,而其他语言是同步的。这里,我们介绍一下javascript中异步的几种方式。
几种异步方式
- 回调函数
- promise
- generator
- async / await
回调函数
回调函数,是早期js中广泛使用的一种回调方式,jquery中的ajax方法就是经典的回调函数模式。
回调函数的写法中,回调是放在函数参数里面的,执行的过程看起来没有同步写法那么明了。
在回调次数多的情况下,可能会造成回调地狱,就是嵌套太深。所以es后面提出的异步方案也是不断对异步做优化。
$.ajax({
url: 'http://binnie.com/getdata',
type: 'GET',
data: {},
success: {
// success todo
},
error: {
// error todo
}
})
复制代码
promise
promise,是es6中提供的一种解决方案,相比于回调函数,promise的写法更加同步化一些~
状态
promise的状态分为三种:
- pending
- resolved
- rejected
状态的变化只有两个过程,并且状态一旦发生改变,就永远不可逆。
- pending -> resolved
- pending -> rejected
基础
Promise是一个构造函数,我们可以使用new来进行实例化的操作。
我们来看一个简单的?
我们可以看到new一个Promise对象的时候,传入的是一个函数,该函数携带两个参数:resolve&reject,分别为成功操作&失败操作,函数内部的操作是同步执行的,所以当执行代码的时候,马上就可以打印出1
new返回的对象是一个Promise对象,Promise对象有几个内置的函数,这几个函数是在Promise的同步函数执行完之后可能触发的函数。
每次执行完其中一个函数,是可以接着链式执行的,因为每次执行完一次,都会返回一个新的Promise对象。
- then
- finally
- catch
let promise = new Promise((resolve, reject) => {
// 这里是同步执行的
console.log(1)
// 异步操作
return resolve('binnie')
});
promise.then((resolve) => {
// 成功回调,即执行resolve()
console.log('then')
},(reject) => {
// 失败回调,即执行reject()
console.log('err')
}).finally(() => {
// 完成回调,无论成功/失败都会到的回调
console.log('finally')
}).catch((err) => {
// 错误回调,前面任何一步抛出的错误
console.log(err)
});
// 1
// then
复制代码
实例方法
实例方法是new Promise返回的对象的原型方法。比如then方法 -> Promise.prototype.then。
then
then方法接受两个参数,第一个为resolve回调,第二个为reject回调
promise.then((resolve) => {
// resolve参数为promise resolve的时候的参数
// 当promise执行resolve时,会进入该回调
},(reject) => {
// reject参数为promise reject的时候的参数
// 当promise抛出异常 或者 执行reject时,会进入该回调
})
复制代码
finally
finally回调函数不接受任何参数,只做finally处理,类似请求的complete操作。
promise.finally(() => {
// resolve or reject 之后的操作
})
复制代码
catch
catch方法,用于捕获异常。
在promise中的异常可以在两个位置进行捕获,then方法的第二个函数 or catch方法。一般情况下,我们会在catch中捕获。
在promise中,错误也是会冒泡的,如果then操作没有添加reject方法,那么错误就会在后面的catch捕获,不过,如果没有对获取进行捕获,错误其实会被promise自己吃掉,而不会暴露在最外面。
promise.then(() => {
console.log('then')
throw new Error()
}).catch((err) => {
console.log(err)
})
复制代码
Promise方法
Promise方法,直接调用即可,返回的也是promise对象。
Promise.all(array)
Promise.all,接受一个数组做为参数,参数中的对象会被转化为promise对象
当数组中的所有promise对象resolve时,all的promise的状态才变为resolve
当数组中的任一promise对象reject时,all的promise的状态就变为reject
Promise.all 类似我们平常使用的 &&
Promise.all([promise,2,3]).then((resolve) => {
// 参数也是一个数组,为all数组对应的resolve参数
// ['binnie',2,3]
console.log(resolve)
}).catch((err) => {
// 错误为第一个reject时抛出的错误
console.log(err)
})
复制代码
Promise.race(array)
Promise.race跟all的用法类似,也是接受一个数组做为参数
数组中最先改变状态的,那个状态就会同步给race的状态
Promise.race([1,promise,3]).then((resolve) => {
// 参数为第一个状态变为resolve的参数
console.log(resolve)
}).catch((err) => {
// 参数为第一个状态变为reject的参数
console.log(err)
})
复制代码
Promise.resolve()
返回一个状态为resolved的promise对象。
Promise.resolve()可携带参数,参数会做为返回对象的参数。
Promise.reject()
返回一个状态为rejected的promise对象。
Promise.reject()可携带参数,参数会做为返回对象的参数。
generator
generator可以理解为一个暂停执行函数。每次执行next()来往下走,直到结束,状态停止。
generator函数的写法,就是在函数后面加一个星号 function*
,在函数内部,可以使用 yield
来进行暂停的操作。
function* foo() {
console.log(0)
yield 1
yield 2
yield 3
return 4
yield 5
}
// 调用generator函数的时候,函数并不会被执行
let f = foo()
console.log(f)
// 调用next时函数才会开始执行,当遇到yield时暂停
// 下一次next时会从上次暂停的位置继续执行
// 当遇到return时 or 函数执行完成时,函数结束
console.log(f.next())
console.log(f.next())
console.log(f.next())
console.log(f.next())
console.log(f.next())
复制代码
async / await
generator相对来说可读性会比较差,于是乎有来async函数,表示函数内部会有异步操作,async函数相当于是generator函数的语法糖。
下面我们来看下async函数的写法
// 定义一个async函数,表明函数内部要执行异步操作
async function fun() {
// await后面跟着异步操作
// 函数会等待await操作完成之后再继续往下走
let obj = await request();
return obj;
}
// async函数执行完之后会返回一个promise对象,函数返回值可再then中取到
let ret = fun()
ret.then((resolve) => {
console.log(resolve)
})
复制代码
当async函数中遇到await时,就会进行等待,然后再执行下一步的操作,如果没必要串行,我们可以改成并行的写法,这样可以节省函数执行时间。
// 串行写法
async function fun() {
let obj1 = await request1();
let obj2 = await request2();
return;
}
// 并行写法
async function fun() {
let obj1 = request1();
let obj2 = request2();
let ret1 = await obj1;
let ret2 = await obj2;
return;
}
// Promise.all写法
async function fun() {
let [obj1, obj2] = await Promise.all([request1(), request2()]) ;
return;
}
复制代码
写在最后
异步操作的核心都是一样的,只是写法不同而已。
具体要采用哪种异步方法,还是要看我们实际的项目需要。