异步编程
js中的单线程是指负责执行代码的线程只有一个,这样设计的初衷是js是用来操作DOM的,多线程会产生DOM操作冲突。
缺点:执行耗时任务时会阻塞代码执行,出现假死状态。
解决:有同步模式和异步模式。JS代码执行是单线程,但是浏览器是多线程的,因此可以借用浏览器的多线程去执行一些耗时任务,如ajax请求。是同步模式还是异步模式是由运行环境提供的API来决定的。
取个例子:一段代码中用promise封装了一个ajax请求,利用浏览器的多线程去请求这个ajax请求,该段代码继续执行,请求完成后,promise.then中的回调函数会添加到代码段的最后去执行。这样,解决了耗时任务阻塞代码执行的问题。
回调函数
回调函数就是将你想要做的事情写在一个函数中,并把这个函数作为另一个函数的参数交给执行者去执行。举个例子:
// 回调函数作为参数交给执行者,让执行者决定想什么情况下执行
function foo(callback) {
setTimeout(function() {
callback()
}, 1000)
}
// 回调函数
function callback() {
console.log('这是我想要做的事情')
}
foo(callback)
Promise
promise初始化
Promise是一个构造函数,promise对象初始化如下:
// 参数是一个函数,函数里面是你需要执行的函数,并根据结果调用相应的函数,
// resolve将状态改为resolved,并将结果传给then中的第一个回调函数参数
// resolve将状态改为rejected,并将结果传给then中的第二个回调函数参数
const p = new Promise(function(resolve, reject) {
resolve('success')
})
// 不同结果的相应回调函数,成功了执行第一个函数,失败了执行第二个函数
p.then(res => {
console.log(res)
}, err => {
console.log(err)
})
用Promise封装Ajax请求:
function ajax (url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.onload = function () {
if(this.status === 200){
resolve(this.response)
}else{
reject(new Error(this.statusText))
}
}
xhr.responseType = 'json'
xhr.open('GET', url)
xhr.send()
})
}
ajax('https://www.ctwing.cn/sysCategory/getCatTreeByCode/homepage_nav').then(res => {
console.log('success', res)
}, err => {
console.log('fail', err)
})
链式调用
如果一个请求的发起需要在另一个请求成功的基础上,那么如果写在前一个then回调函数中,同样会造成地狱回调的问题。promise提供了链式调用来解决这个问题。
promise.then返回的是一个全新的promise对象(不是像以往的那种return this来实现的)。链式调用时,每一个then方法都在为上一个then返回的promise对象添加回调。
promise.then中也可以手动return一个promise对象作为下一个then方法的promise对象。
异常处理
在链式调用中,如果异常捕获写在.then的第二个参数中,则只能捕获当前promise的异常。如果写在链条的最末端,那么可以捕获到链条上的所有异常,链条上任何一个异常都会被传递,直至被捕获。
静态方法
Promise.resolve() 快速创建一个状态为resolved的promise对象
- 参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象
- 参数是一个 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。
- 参数是一个有then方法的对象,将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法
- 参数为空,直接返回一个resolved状态的 Promise 对象,resolvedValue为undefined
Promise.reject() 快速创建一个状态为reject的promise对象
const p = new Promise((resolve, reject) => {
resolve('a promise Object')
})
const p1 = Promise.resolve('foo')
// Promise {<resolved>: "foo"}
// __proto__: Promise
// [[PromiseStatus]]: "resolved"
// [[PromiseValue]]: "foo"
const p2 = Promise.resolve(p)
console.log(p2 === p)
// true
const p3 = Promise.resolve({
name: 'tom',
then: function (resolve, reject) {
console.log('name is: ' + this.name)
resolve(this.name)
}
})
// name is: tom 立即执行then方法
// Promise {<resolved>: "tom"}
// __proto__: Promise
// [[PromiseStatus]]: "resolved"
// [[PromiseValue]]: "tom"
const p4 = Promise.resolve()
// Promise {<resolved>: undefined}
// __proto__: Promise
// [[PromiseStatus]]: "resolved"
// [[PromiseValue]]: undefined
Promise.all() 参数为promise对象数组。返回一个新的promise对象,等待所有结果成功,新对象才返回成功,并将所有的结果传递给新对象,如果其中一个失败,则新对象失败,并返回第一个失败的结果
Promise.race() 返回第一个完成的任务的结果
const pro1 = new Promise((resolve, reject) => {
setTimeout(function () {
resolve('success1')
}, 1000)
})
const pro2 = new Promise((resolve, reject) => {
setTimeout(function () {
resolve('success2')
}, 3000)
})
const pro3 = new Promise((resolve, reject) => {
setTimeout(function () {
reject('failed1')
}, 2000)
})
const proAll = Promise.all([pro1, pro2])
// Promise {<resolved>: Array(2)}
// __proto__: Promise
// [[PromiseStatus]]: "resolved"
// [[PromiseValue]]: Array(2)
// 0: "success1"
// 1: "success2"
// length: 2
// __proto__: Array(0)
const proRace = Promise.race([pro1, pro2, pro3])
// Promise {<resolved>: "success1"}
// __proto__: Promise
// [[PromiseStatus]]: "resolved"
// [[PromiseValue]]: "success1"
小练习:带有timeout的请求结果
function request () {
return new Promise((resolve, reject) => {
setTimeout(function () {
resolve('success')
}, 2000)
})
}
function timeout () {
return new Promise((resolve, reject) => {
setTimeout(function () {
reject('time out!')
}, 500)
})
}
const newRequst = Promise.race([request(), timeout()])
newRequst.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})