都2020年了,你还没有搞懂异步、回调、promise么?(一)

344 阅读4分钟

同步函数

  • 通常来说,浏览器在执行js代码的时候,会按照代码顺序逐行执行。也就是说,上一行代码没有执行完毕,是不会继续向下执行的,而是会一直等待到这行代码返回了执行结果以后才会执行
// 它既可以当做参数,也可以当返回值
// todo 同步函数  一行一行执行,当代码执行完毕,这个函数就调用完毕
function add(x,y){
    return x+y
}
add(10,20)

异步

  • javascript语言的执行环境的'单线程'的.

  • 所谓'单线程',就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。

  • (实际上我认为计算机从微观上讲都是单线程的,因为一台机器在同一时间只能做一件事情。然而从宏观上讲,在我们人的对时间的感知范围内,认为机器可以在一个时间做多个事情。这就是我们感知的异步/多线程/并发。本质上机器运算也是按照先后顺序来的,这也就为什么会有微积分等这样的学科来研究事物的本质了。)

  • 同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;

  • 异步任务:不进入主线程,而进入任务队列中的任务,只有任务队列通知主线程,某个异步任务可以执行了,这个任务才会进入主线程执行。

  • 事件循环(Event Loop):只有执行栈中的所有同步任务都执行完毕,系统才会读取任务队列,看看里面的异步任务哪些可以执行,然后那些对应的异步任务,结束等待状态,进入执行栈,开始执行。

异步函数

// 一般情况下,把函数当做参数的目的就是为了获取函数内部的异步操作结果
/如果需要得到一个函数内部异步操作的结果,这个时候必须通过回调函数来获取
//在调用的位置传递一个函数进来,在封装的函数内部调用传进来的函数

// js单线程、事件循环
console(1)
setTimeout(()=>{//不会等它 到3 就已经结束了
  console.log(2)
},100)
console.log(3)
//输出结果:
1
3
2//就算定时器的时间为0  也是这样的输出结果

异步编程

如何熟练达到像定义一个变量一样来封装一个带有回调函数的方法

setTimeout
readFile
writeFile
ajax
//这种情况必须通过 :回调函数
function add(x,y,callback){//下层调用
  /相当于:
  //var x =10 ,
  //var y = 20,
  //var callback= function(result) {console.log(result)}
    setTimeout(()=>{//不会等它 到3 就已经结束了
    var result =  x+y
    callback(result)//必须callback
    },100)
}

//上层定义
add(10,20,function(result){
  console.log(result)
})

async await用同步的方式写异步

同步:
var result = await xxx(promise对象)
异步:有回调函数
axios.get('/xxx',function()=>{})

举一个栗子

先创建一个person对象用于理解

const person={
    eat:()=>{console.log('吃饭...')},
    sleep:()=>{console.log('睡觉...')},
    getUp:()=>{console.log('起床啦...')}
}

接着分别调用其内部定义的方法:

person.eat() // 吃饭...
person.sleep() // 睡觉...
person.getUp() // 起床啦...
  • 这里person对象每次调用内部的方法时,都需要等待上一个调用的方法执行结束后才会执行,就好像一个人一般来说是不能边吃饭边睡觉的,必须要等吃完了饭才能去睡觉。那么这种代码的执行方式就被称为同步。

  • 很显然,异步就是与同步相对的执行方式,也就是调用代码后不等待调用结果,而是立即返回,再在将来通过某种手段来获取调用结果。

const person = {
    eat: () => { console.log('吃饭...') },
    sleep: () => { console.log('睡觉...') },
    getUp: () => { console.log('起床啦!') },
    setAlarmClock: function setAlarmClock(time) {
        setTimeout(() => {
            this.getUp()
        }, time)
        console.log(`我定了一个闹钟,${time}ms后叫我起床。`)
    }
}

person.setAlarmClock(1000) // 我定了一个闹钟,1000ms后叫我起床。
person.eat() // 吃饭...
person.sleep() // 睡觉...
// (1s后)起床啦!
  • 这里person对象首先执行了setAlarmClock函数,也就是定了个闹钟,而它定完闹钟之后并不会坐在闹钟面前等着它响,而是先去吃饭、睡觉,等1s钟后闹钟响了再起床。

  • 这种执行方式就叫做异步。

  • 一句话概括,就是同步等结果,异步不等结果

回调函数

  • 什么是回调呢?

  • 上面提到过,异步代码调用后不用等待调用结果,而是将来通过某种手段来获取结果,这里的某种手段就是回调。

  • 回调函数遵循以下两条原则:

    • 写出来的方法不是给自己调用的,而是给别人调用的(这里指的别人可以指的是其他的函数)
    • 方法会在将来的某个时间被其他人执行,而不是开发人员自己手动调用执行。
    //只要不是自己直接调用的方法都算是将来执行,例如fn()表示我现在立即执行了fn()函数
    
    • 一句话总结:回头我再调用,不是现在调用。

回调一般出现在异步编程中,用于得到异步处理的结果。回调函数可以作为参数传递到其他函数中,让其在合适的时候调用

// 一般情况下,把函数当做参数的目的就是为了获取函数内部的异步操作结果
/如果需要得到一个函数内部异步操作的结果,这个时候必须通过回调函数来获取
//在调用的位置传递一个函数进来,在封装的函数内部调用传进来的函数
  • 假如闹钟响了之后还需要获取当前的睡眠状态,来判断要不要接着睡:
const person = {
    eat: () => { console.log('吃饭...') },
    sleep: () => { console.log('睡觉...') },
    getUp: () => { console.log('起床啦!') },
    setAlarmClock: function setAlarmClock(time, callback) {
        setTimeout(() => {
            this.getUp()
            const state = '困成狗'
            callback(state)
        }, time)
        console.log(`我定了一个闹钟,${time}ms后叫我起床。`)
    }
}

person.setAlarmClock(1000, (state) => {
    if (state === '困成狗') {
        console.log('再睡一会...')
    } else {
        console.log('赶紧起...')
    }
}) // 我定了一个闹钟,1000ms后叫我起床。
person.eat() // 吃饭...
person.sleep() // 睡觉...
// (1s后)起床啦!
//  再睡一会...
  • 这里执行setAlarmClock函数时会传递一个参数callback也就是回调函数,在闹钟响了之后就会执行这个函数,并将当前的状态作为参数传递到这个函数中。这样就能在回调函数中得到这段异步代码的结果并根据结果做出不同的操作了。

参考文章:

juejin.cn/post/684490… es6.ruanyifeng.com/#docs/promi… juejin.cn/post/684490…