概述
同步模式
异步模式
回调函数
所有的异步编程方案的根基都是回调函数
回调函数其实就是 todoList,就是你想要做的事
调用者,执行者,由调用者定义,交给执行者执行的函数就是回调函数
举个🌰:
女朋友 想吃滑蛋叉烧饭,然后把做菜的步骤发给男朋友之后,就去看电视剧了,然后男朋友就按照女朋友给的步骤,就去做菜了,做完菜后端给女朋友吃。整个过程,女朋友就是调用者,男朋友就是执行者,整个过程就是一个回调函数,因为女朋友并没有在等待,就去看电视剧了!!
Promise的概述
Promise的基本用法
// Promise的基本示例
// 需要传入一个函数作为参数,这个函数会在构造Promise的过程中,**同步**执行!!
// 这个函数内部,接收两个参数,resolve和reject,这两个参数都是函数,
// 因为Promise的状态一旦确定过后,就不会再被修改,所以二者只会执行其中之一
const promise = new Promise(function (resolve, reject) {
// 用于兑现承诺,下面的二者方法只能二选一
resolve(100) // 承诺达成
// reject(new Error('Promise rejected')) // 承诺失败
})
// 使用then方法分别指定onFullfilled回调函数,和onReject回调函数
// 这里的promise的回调会先进入回调队列进行排队,等主执行栈里的全部同步代码执行完毕之后才会执行
promise.then(function(value) {
// 承诺成功的回调
console.log('resolved', value)
}, function(error) {
// 承诺失败的回调
console.log('rejected', error)
})
console.log('end')
// 打印结果
// end
// resolved 100
Promise的使用案例
// Promise 方式的 AJAX
function ajax(url) {
// 对外返回一个Promise对象,即对外做出一个承诺
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = function() {
if (this.status === 200) {
resolve(this.response)
} else {
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
Promise的常见误区
- 嵌套使用的方式是使用Promise的最常见的误区
Promise的链式调用
.then方法返回的是一个全新的Promise
每一个then方法都是为了上一个then返回的Promise对象添加状态明确过后的回调
- Promise对象的then方法会返回一个全新的Promise
- 后面的then方法就是在为上一个then返回的Promise注册回调 Question:这里的注册回调是插入到回调队列里吗???
- 前面的then方法中回调函数的返回值会作为后面的then方法回调的参数
- 如果回调中返回的是Promise,那后面then方法的回调会等待它的结束
Promise异常处理
onRejected在Promise失败了,或者是出现异常时都会被调用,也可以使用catch方法进行对onRejected方法的注册
// then方法传入两个参,一个onFullfilled,一个onRejected
ajax('/user.json').then(function onFullfilled(value) {
console.log('onFullfilled', value)
}, function onRejected(err) {
console.log('onRejected', value)
})
// 这里与上面等价,其实catch方法就是then方法的别名
ajax('/user.json')
.then(function onFullfilled(value) {
console.log('onFullfilled', value)
}) // 返回一个新的Promise,下面的catch是捕获返回新Promise的错误
.catch( function onRejected(err) {
console.log('onRejected', value)
})
// 等价于 then 方法第一个参数传的undefined
// .then(undefined, function onRejected(err) {
// console.log('onRejected', err)
// })
区别:
- PromiseA的then方法传递onRejectd函数参数时,then方法里面的 onFullfilled函数参数里面返回的是一个新的PromiseB时,onRejected方法是没办法捕获到的,因为onRejected函数是对当前PromiseA做异常处理捕获
- 而用catch方法进行对异常处理的捕获是可以解决1上面的问题,那是因为,catch是then方法的别名,也就是执行了另一个then方法,前面说的then方法的是前一个PromiseA返回的新的PromiseB进行注册回调,也就是说能对新的PromiseB进行异常处理
Promise的静态方法
- Promise.resolve() 快速的把一个值转换为一个Promise对象
- Promise.reject() 快速的创建一个一定是失败的Promise对象
// resolve
Promise.resolve('foo') // 直接返回一个状态为 Fulfilled的Promise对象,'foo'就会作为这个Promise对象所返回的值
.then(function (value) { // value就是 foo
console.log(value)
})
// 等价于
new Promise(function (resolve, reject) {
resolve('foo')
})
// 使用resolve方法传入的是一个Promise对象,会返回一个原样的Promise
var promise = ajax('/user.json')
var promise2 = Promise.resolve(promise)
console.log(promise === promise2) ==> true
Promise.resolve({
// 传入一个 和 Promise一样的then方法里,thenable(可以被then的对象)
then: function(onFulfilled, onRejected) {
onFulfilled('foo')
}
}).then(function (value) {
console.log(value) ==> foo
})
Promise的并行执行
并行请求的困难点在于,没办法判断是否全部执行完毕,需要用个东西来判断是否全部执行完毕,就很骚操作
- Promise.all([]) // 里面参的是一个数组,数组里面的是一个个Promise,等待所有Promise执行结束才会返回新的Promise对象,且是所有Promise成功完成才会成功返回
// 使用Promise.all同步执行多个Promise
var promise = Promise.all([
ajax('/api.json'),
ajax('user.json')
])
promise.then(function (value) {
console.log(value)
}).catch(function (error) { // 只要有一个Promise失败或者出现异常就会执行
console.log(error)
})
- Promise.race() 里面参的是一个数组,数组里面的是一个个Promise,它不会像all一样等所有Promise执行结束才会往下执行,而是只要有一个Promise完成就会返回新的Promise对象
const request = ajax('/api/posts.json')
const timeout = new Promise((resolve, reject) {
setTimeout(() => reject(new Error('timeout')))
})
// 使用浏览器的 限速功能进行处理
Promise.race([
request,
timeout
]).then(value => {
console.log(value)
}).catch(error => {
console.log(error)
])
Promise执行时序
JS分为同步任务和异步任务,异步任务又可以分为宏任务,微任务
- task同步任务:所有同步任务都会在主线程中执行,形成一个执行栈,如图callstack,执行栈会把所有的同步任务执行完毕才会往下调用微任务
- macrotask宏任务:其实可以理解为每个执行栈执行的代码就是一个宏任务,
1. 每一个task会从头到尾将这个任务执行完毕,不会执行其他的
2. 浏览器为了能够使得JS内部task与DOM任务能够有序的执行,会在一个task执行结束后,在下一个task执行开始之前,对页面进行渲染
3. 常见的宏任务:setTimeout,setInterval,IO
4. 宏任务执行顺序也是通过队列进行存放的,先进先出机制
- microtask微任务:可以理解为在当前task执行结束后立即执行的任务是同步任务里所有代码执行完毕之后,紧接着开始执行的任务,
1. 也就是说,在当前task任务后,在下一个task之前,渲染之前
2. 所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为不需要渲染
3. 即在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask执行完毕(在渲染之前)
4. 常见的微任务:Promise && MutationObserver && process.nextTick
所以,总结下运行机制:
- 执行一个宏任务(栈中没有就从事件队列中获取)
- 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
- 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
- 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
- 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。