文章内容输出来源:拉勾教育前端高薪训练营
异步编程是为了解决同步模式的一些痛点,同步模式中任务是依次执行,后一个任务必须要等待前一个任务结束后才能开始执行,当某个函数耗时过长时就可能造成页面的假死和卡顿,而异步编程中,后一个任务不会去等待前一个任务结束后才开始,当前一个任务开启过后就立即往后执行下一个任务。耗时函数的后续逻辑会通过回调函数的方式定义。在内部,耗时任务完成过后就会自动执行传入的回调函数。
1.同步模式和异步模式
- 同步模式代码按顺序执行,任务执行完代码才会继续往下走,可能让页面卡顿和假死
// 同步模式
console.log('global begin')
function bar () {
console.log('bar task')
}
function foo () {
console.log('foo task')
bar()
}
foo()
console.log('global end')
// global begin
// foo task
// bar task
// global end
- 异步模式就是下达这个任务开启的指令之后代码就会继续执行,代码不会等待任务的结束
// 异步模式
console.log('global begin')
// 延时器
setTimeout(function timer1 () {
console.log('timer1 invoke')
}, 1800)
// 延时器中又嵌套了一个延时器
setTimeout(function timer2 () {
console.log('timer2 invoke')
setTimeout(function inner () {
console.log('inner invoke')
}, 500)
}, 1000)
console.log('global end')
// global begin
// global end
// timer2 invoke
// inner invoke
// timer1 invoke
2.回调函数
由调用者定义,交给执行者执行的函数,回调函数的实际就是把函数作为参数传递,缺点是不利于阅读,执行顺序混乱
function foo(callback) {
setTimeout(function(){
callback()
}, 3000)
}
foo(function() {
console.log('这就是一个回调函数')
console.log('调用者定义这个函数,执行者执行这个函数')
console.log('其实就是调用者告诉执行者异步任务结束后应该做什么')
})
3.Promise、微任务和宏任务
3.1 Promise
为了避免异步回调地狱,CommonJS社区提出了Promise的规范
- Promise是一个对象,用来表述一个异步任务执行之后是成功还是失败
- 任务状态确定后不可以再次更改,pending => fulfilled || pending => rejected
- promise对象then方法,返回了全新的promise对象。可以再继续调用then方法,如果return的不是promise对象,而是一个值,那么这个值会作为resolve的值传递,如果没有值,默认是undefined
- 后面的then方法就是在为上一个then返回的Promise注册回调
- 前面then方法中回调函数的返回值会作为后面then方法回调的参数
- 如果回调中返回的是Promise,那后面then方法的回调会等待它的结束
Promise静态方法:Promise.resolve(),Promise.reject(),Promise.all()和Promise.race(),接收一个数组,all等待所有任务结束,race只等待第一个任务结束
使用promise封装ajax
function ajax(url) {
return new Promise(function (resolve, reject) {
const 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()
})
}
3.2 微任务和宏任务
宏任务为宿主发起(Node,浏览器),微任务为js引擎发起;微任务Aa必然是某个宏任务A执行的时候创建的,在微任务Aa执行完毕后,开始下一个宏任务B之前,会将A执行期间产生的所有微任务都执行完毕;在node环境下,process.nextTick的优先级高于Promise,也就是说:在宏任务结束后会先执行微任务队列中的nextTickQueue,然后才会执行微任务中的Promise
- 浏览器宏任务:setTimeout,setInterval,requestAnimationFrame
- 浏览器微任务:MutationObserver,Promise.then catch finally
- Node宏任务:setTimeout,setInterval,setImmediate
- Node微任务:process.nextTick,Promise.then catch finally
- promise的构造函数是同步执行,promise.then中的函数是异步执行
// 1-7-8-2-4-5-9-11-12
//主线程直接执行
console.log('1');
//丢到宏事件队列中
setTimeout(function() {
console.log('2');
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
//主线程直接执行
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
//丢到宏事件队列中
setTimeout(function() {
console.log('9');
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
3.3 模拟Promise
// 定义promise的三种状态,pending(等待),fulfilled(成功),rejected(失败)
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor (executor) {
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
// 初始化promise状态
status = PENDING
// 成功返回值
value = undefined
// 失败返回值
err = undefined
// 存储异步成功回调函数
successCallback = []
// 存储异步失败回调函数
failCallback = []
static all (arr) {
const results = []
const len = arr.length
let index = 0
return new MyPromise((resolve, reject) => {
const addData = (key, value) => {
results[key] = value
index += 1
if (index === len) {
resolve(results)
}
}
for (let i = 0; i < len; i += 1) {
if (arr[i] instanceof MyPromise) {
arr[i].then(value => {
addData(i, value)
}, err => {
reject(err)
})
} else {
addData(i, arr[i])
}
}
})
}
static race (arr) {
const len = arr.length
return new MyPromise((resolve, reject) => {
for (let i = 0; i < len; i += 1) {
if (arr[i] instanceof MyPromise) {
arr[i].then(value => {
resolve(value)
}, err => {
reject(err)
})
} else {
resolve(arr[i])
}
}
})
}
static resolve (value) {
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
static reject (err) {
return new MyPromise((resolve, reject) => reject(err))
}
resolve = value => {
// 当状态不为等待时阻止向下执行
if (this.status !== PENDING) return
this.status = FULFILLED
this.value = value
while (this.successCallback.length) this.successCallback.shift()()
}
reject = err => {
// 当状态不为等待时阻止向下执行
if (this.status !== PENDING) return
this.status = REJECTED
this.err = err
while (this.failCallback.length) this.failCallback.shift()()
}
then (successCallback, failCallback) {
// 将then的参数变为可选参数
successCallback = successCallback || (value => value)
failCallback = failCallback || (err => { throw err })
// 为了实现链式调用,返回一个新的promise
const promise = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const x = successCallback(this.value)
resolvePromise(promise, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = failCallback(this.err)
resolvePromise(promise, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
} else {
this.successCallback.push(() => {
setTimeout(() => {
try {
const x = successCallback(this.value)
resolvePromise(promise, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
})
this.failCallback.push(() => {
setTimeout(() => {
try {
const x = failCallback(this.err)
resolvePromise(promise, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
})
}
})
return promise
}
finally (callback) {
return this.then(value => {
return MyPromise.resolve(callback()).then(() => value)
callback()
}, err => {
return MyPromise.resolve(callback()).then(() => { throw err })
})
}
catch (callback) {
return this.then(undefined, callback)
}
}
/*
判断x的值是普通值还是promise对象
普通值直接调用resolve
promise对象根据返回结果决定调用resolve还是reject
*/
function resolvePromise (promise, x, resolve, reject) {
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise #<MyPromise>'))
}
if (x instanceof MyPromise) {
x.then(value => {
resolve(value)
}, err => {
reject(err)
})
} else {
resolve(x)
}
}
module.exports = MyPromise
4.生成器函数generator
基本使用
function * foo () {
try {
const res = yield 'foo'
console.log(res) // bar
} catch (e) {
console.log(e)
}
}
const generator = foo()
const result = generator.next()
console.log(result) // {value: "foo", done: false}
generator.next('bar')
模拟异步变同步
function ajax(url) {
return new Promise(function (resolve, reject) {
const 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()
})
}
function * main () {
try {
const users = yield ajax('api/users.json')
console.log(users)
const posts = yield ajax('api/posts.json')
console.log(posts)
} catch (e) {
console.log(e)
}
}
function co (generator) {
const m = generator()
const resultAjax = m.next()
function resultLoop (result) {
if (result.done) return
result.value.then(data => {
const res = m.next(data)
resultLoop(res)
}, err => {
m.throw(err)
})
}
resultLoop(resultAjax)
}
co(main)