前言
异步编程采用回调函数的方式,一不小心就会陷入回调地狱
$.get('api1',function(data1) {
$.get('api2',function(data2) {
$.get('api3',function(data3) {
})
})
})
为了避免回调地狱,Common JS社区提出了Promise规范
Promise在ES2015中,被标准化,成为语言规范
定义
定义:Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值
graph LR
Promise -->Pending
Pending -->Fulfilled
Pending -->Rejected
Fulfilled -->onFulfilled
Rejected --> onRejected
通过一个故事了解 主线程,Promise,Event loop
故事背景:我是一名连锁餐馆唯一的厨子(主线程)
我对Promise(众多服务员其中之一)说:帮我买一瓶500ML的*牌子的陈醋。
Promise说:好的,这个任务我接下来了。
然后我就切了一个菜就去休眠了(因为其他活都干完了,并且我要分配任务不能离开)。
Promise去买醋了。
Event Loop在作为我的监工,就在等待着Promise回来,Promise回来后,就把我叫醒。
情况A:
Promise对我说:我买的你要的东西了,给你。
我拿到醋就开始烹饪了。
情况B:
Promise对我说,因为*牌子没有货了,我没有买到你想要的东西。
然后去做补救措施。
上面的故事中
我(主线程)在老板(运行环境)允许的范围内,分配人力。
Promise是一个很有契约精神的人,他承诺了帮我买东西,按照我的指令严格执行,并且最后会告诉我最终结果。
Event Loop就是老板派下来的监工(店长),看我闲下来,并且有活的时候就会叫我来干活。 当然我忙的时候店长也不会来打扰我的工作进度。即使Promise完成了我分配的任务,店长也会让他先等着。
Promise封装ajax
function ajax (url) {
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()
})
}
const promise = ajax('/api/users.json')
promise.then((res)=> { console.log(res) }, err => { console.log(err) })
promise的本质就是使用回调函数的方式,去定义异步任务结束后需要执行的任务,只不过异步函数是通过then方法传递进去的。
Promise常见误区及链式调用
如果请求B必须依赖请求A返回的结果,很多人又会写成回调地狱,其实promise是可以链式调用的,让我们的异步任务代码扁平化。
ajax('/api1')
.then(function (value) {
console.log(1)
return ajax('/api/value')
}) // => Promise
.then(function (valueRes) {
console.log(2)
return ajax('/api/valueRes')
}) // => Promise
.then(function (valueResRes) {
console.log(3333)
console.log(valueResRes)
return valueResRes
}) // => Promise
.catch(function (err) {
console.log(err)
})
- Promise对象的then方法会返回一个全新的 Promise对象
- 后面的then方法就是在为上一个then返回的 Promise注册回调
- 前面then方法中回调函数的返回值会作为后面then方法回调的参数
Promise静态方法
Promise.resolve('foo')
// 等价于
new Promise(function (resolve, reject) {
resolve('foo')
})
Primose并行执行
// api 返回多个接口,然后拿到地址,并行请求其余接口
// 等待所有任务执行结束,进入then
ajax('/api')
.then(value => {
const urls = Object.values(value)
const tasks = urls.map(url => ajax(url))
return Promise.all(tasks)
})
.then(values => {
console.log(values)
})
Promise.race
// Promise.race 实现500ms超时控制
const request = ajax('/api/posts.json')
const timeout = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('timeout')), 500)
})
Promise.race([
request,
timeout
])
.then(value => {
console.log(value)
})
.catch(error => {
console.log(error)
})
后记
promise的常用方法,本篇内容介绍的差不多了。 多个promise链式调用的执行顺序,即:宏任务、微任务。放在下一篇讲解。