总结-异步代码的执行

412 阅读2分钟

异步代码和同步代码执行顺序

同步代码优先于异步代码执行

例如:

setTimeout(()=>{
    console.log(1)
},0)
console.log(2)
// 输出结果: 2 1

用回调函数接收异步代码的返回值

异步代码中retrun 没有意义,例如

function fn() {
    setTimeout(() => {
        return 1
    }, 0)
    // 没有返回值,等效于return undefined
}
console.log(fn())//undefined

所以,可以用回调函数接收异步代码的返回值,例如

function fn(callback) {
    setTimeout(() => {
        callback(1) //执行回调函数
    }, 0)
}
fn((data)=>{
    console.log(data)//得到数据
})

将回调函数作为对象的属性

将异步函数以对象的形式,传到存在异步代码的函数中,方便接受多种情况下的返回结果,例如

function ajax(options) {
    setTimeout(() => {
        options.success(1) //执行失败的回调
        // options.fail(404) //执行成功的回调
    }, 0)
}
ajax({
    success(data){
        console.log(data)
    },
    fail(error){
        console.log(error)
    }
})

回调地狱的形成

如果多个请求存在前后次序的调用问题,代码上的多层嵌套会形成回调地狱

function ajax(options) {
    setTimeout(() => {
        options.success(1)
    }, 0)
}
ajax({
    url: '/url1',
    success(data1){
        ajax({
            url: '/url2',
            success(data2){
                ajax({
                    url: '/url3',
                    success(data3){
                    }
                })
            }
        })
    }
})

使用Promise解决回调地狱的问题

Promise的语法

let p = new Promise((reslove, reject)=>{
    setTimeout(() => {
        // reslove(1) //成功的回调
        reject(404) //失败的回调
    }, 0)
})

p.then((data)=>{
    //接收成功回调的数据
    console.log(data)
}).catch((error)=>{
    //接收失败的回调的数据
    console.log(error)
})

理解Promose的写法

最简单的手写Promise

//定义Promise构造函数
function Promise(fn){
    setTimeout(() => {
        fn(this.success,this.fail)
    }, 0)
}
//注册成功的回调函数
Promise.prototype.then = function(success) {
    this.success = success
    return this
}
//注册失败的回调函数
Promise.prototype.catch = function(fail) {
    this.fail = fail
    return this
}

延伸阅读

面试官:“你能手写一个 Promise 吗”

BAT前端经典面试问题:史上最最最详细的手写Promise教程

解决回调地狱

function axios () {
     return new Promise((reslove, reject)=>{
         setTimeout(() => {
             reslove(1)
         }, 0)
     })
}
//错误示范,依然存在回调地狱
axios({
    url: '/url1'
}).then((data1)=>{
    axios({
        url: '/url2'
    }).then((data2)=>{
        axios({
            url: '/url3'
        }).then((data3)=>{
            
        })
    })
})
//正确示范,利用链式调用解决回调地狱
axios({
    url: 'url1'
}).then((data1)=>{
    return axios({
        url: 'url2'
    })
}).then((data2)=>{
    return axios({
        url: 'url3'
    })
}).then((data3)=>{
    console.log(data3)
})

使用异步函数简化Promise的接收

async function fn () {
    let data1 = await axios({url: 'url1'})
    let data2 = await axios({url: 'url2'})
    let data3 = await axios({url: 'url3'})
}

Javascript代码运行机制

什么是EventLoop?

JavaScript 主线程从“任务队列”中读取异步,任务的回调函数,放到执行栈中依次执行。这个过程是循环不断的,所以整个的这种运行机制又称为 EventLoop(事件循环)

面试题

//1.异步代码,交给浏览器或nodejs    
//1.1执行完成-放到任务队列
setTimeout(() => {
    console.log('A')
}, 0)
//2.主线程执行同步代码
for (let i = 0; i < 1000; i++) {
   console.log(i)
}
//3.到任务队列取异步结果
//A-最后面

宏任务和微任务

面试题

优先级:同步代码>异步代码(微任务>宏任务) 执行结果:1 3 5 3 2

console.log(1)//同步代码
setTimeout(() => {
    console.log(2)//异步代码,宏任务
}, 0)
let p = new Promise((reslove)=>{
	console.log(3)//同步代码,微任务
    reslove()
})
p.then(()=>{
    console.log('4')//异步代码,微任务
})
console.log(5)//同步代码