回调函数、promise、async的异步解决方案、Event Loop

什么是异步

异步就是把任务分成上下两段,先执行任务上半段,在转而执行其他同步任务。等到其他同步任务全部执行完毕后 || 异步状态发生了改变,再继续执行任务下半段。

回调函数

在promise出来之前,javascript处理异步一般通过回调函数,但是存在一个问题,回调函数一多的话,代码的耦合性就高了,一步错步步错,不容易维护,俗称:回调地狱。

promise是es6的一种异步解决方案

promise解决了回调地狱的问题,Promise是一个对象,它的内部保存了未来才会结束的事件的结果。
promise对象有三种状态:pending、resolved、rejected,一旦状态改变就不会在改变了。
Promise接受一个函数,这个函数又有两个参数,分别为resolve、reject函数,它们是由Javascript引擎提供的。

promise对象有then方法,接受两个函数参数:

  1. 当promise对象的状态变为resolved的时候,把resolve()传递的值传给then的第一个函数执行
  2. 当promise对象状态变为rejected时候,把reject()传递的值传给then的第二个函数执行
  3. then方法返回一个新的promise对象,和原来的promise不是一个对象。所以,then方法,支持链式操作。

catch方法

我们一般不写then的第二个函数参数,使用promise的catch方法。

let promise = new Promise(function(resolve,reject){
	setTimeout(function(){
		try{
			console.log(123)	
			resolve('the first')
		}catch(e){
			reject(e)
		}
	},1000)
})

console.log(promise)
//promise.then(
//msg=>{
//	console.log(msg)
//},
//err=>{
//	console.log(err)
//}
//)
//catch
promise.then(msg=>{
	console.log(msg)
}
).catch(err=>{
	console.log(err)
})
console.log(456)


Promise.all()、Promise.race()

在这里插入图片描述
在这里插入图片描述

Promise.resolve()、Promise.reject()

const p = Promise.resolve('成功了')
p.then(msg=>console.log(msg))
//相当于下面的写法
const p2 = new Promise(function(resolve,reject){
	resolve('成功了')
})
p2.then(msg=>console.log(msg))

const p3 = new Promise(function(resolve,reject){
	reject('失败了')
})
p3.then(msg=>console.log(msg))
  .catch(err=>console.log(err))
 
const p4 = Promise.reject('失败了')
p4.then(msg=>console.log(msg))
 .catch(err=>console.log(err))

async函数

async函数是Generator生成器函数的语法糖,它相较generator函数来说有哪些改进之处:

  1. 内置了执行器,不需要像generator函数一样,要通过next()才能往下执行
  2. async的语义更清晰,async代表了函数中有异步操作,await表示后面的表达式需要等待执行的结果。
  3. async函数返回的是promise对象,而generator函数返回的是iterator对象。你可以很方便的调用then()函数处理异步。

为什么出现async函数呢?

虽然promise解决了回调地狱的问题,但是通过then处理异步回调,写起来还是很麻烦。async函数相比家promise来说,简洁不少。

不过,async也有它的缺点:

await会把异步变成同步,如果两个异步操作没有关联性(继发性),可以使用Promise.all([p1,p2]),让它们并行。这样,性能会快点。

async继发、并发

	//继发
    console.time('start')
    async function fn() {
        let res = await new Promise(res=>{res(123)})
        let res2 = await Promise.resolve(res+'456')
        console.log(res)
        console.log(res2)
        console.timeEnd('start')
    }

    console.log(fn())
    console.log(789)

在这里插入图片描述

	//并发
	console.time('start')
    async function fn() {
        let p1 = Promise.resolve(123)
        let p2 = Promise.resolve(456)
        let res = await p1
        let res2 = await p2
        console.log(res)
        console.log(res2)
        console.timeEnd('start')
    }

    console.log(fn())
    console.log(789)

在这里插入图片描述

	//并发2
	console.time('start')
    async function fn() {
        let p1 = Promise.resolve(123)
        let p2 = Promise.resolve(456)
        let res3 = await Promise.all([p1,p2])
        console.log(res3)
        console.timeEnd('start')
    }

    console.log(fn())
    console.log(789)

在这里插入图片描述

异步执行顺序、Event Loop

	setTimeout(function (x) {
        console.log(x)
    },0,789)

    console.log(456)
//456--->789
//分析:虽然setTimeout的第二个参数是0,但是不会立即执行里面的函数。
//这里setTimeout的工作过程:0代表了立即把函数放到task队列,但是不会立即
//执行。需要等到其他同步任务执行完毕,Event Loop会把task队列中的函数拿到
//执行栈中执行。
	let promise = new Promise(function (resolve,reject) {
        resolve(123)
        console.log(789)
    })
    console.log(promise)
    promise.then(msg=>console.log(msg))

    console.log(456)
// 789--->Promise {<resolved>: 123}--->456--->123

//分析:先立即执行Promise中的函数,resolve(123)会让promise对象状态变为
//成功,打印789,执行完异步任务上半段,打印promise对象,状态为成功,值为
//123。这个时候,不会去执行then中的回调。因为,异步任务的执行是先执行完上
//半段,再转而执行其他同步任务,等到其他同步任务都执行完毕时,如果异步任务
//上半段执行返回的状态改变了,再去执行下半段回调函数。
    let promise = new Promise(function (resolve,reject) {
        setTimeout(resolve,2000,123)
        console.log(789)
    })
    console.log(promise)
    promise.then(msg=>console.log(msg))

    console.log(456)
// 789--->Promise {<pending>}--->456--->123
//分析:2秒过后会把resolve函数放到task队列中,打印789,打印promise,
//打印456.同步代码执行完毕,从task队列拿出resolve函数放到执行栈调用,
//状态改变了,执行then
    async function fn() {
        console.log(789)
        let res = await 123
        console.log(res)
    }

    console.log(fn())
    console.log(456)
//789--->Promise {<pending>}--->456--->123
//分析:打印789,await代表异步操作,后面的表达式不是promise对象,通过
//Promise.resolve(123)把它转换为promise对象,执行完这个异步任务的
//上半段。天跳出async函数执行别的同步代码。打印async的返回值是个pending
//状态的promise对象,打印456.同步代码执行完毕,回到await处,
//Promise.resolve(123)状态已经改变,res的值就是promise对象的值,
//打印res
let a = 0
let b = async () => {
  a = a + await 10
  console.log('2', a) // -> '2' 10
}
b()
a++
console.log('1', a) // -> '1' 1

//首先函数 b 先执行,在执行到 await 10 之前变量 a 还是 0,因为 await 
//内部实现了 generator ,generator 会保留堆栈中东西,所以这时候 a = 0 
//被保存了下来
//因为 await 是异步操作,后来的表达式不返回 Promise 的话,就会包装成 
//Promise.reslove(返回值),然后会去执行函数外的同步代码
//同步代码执行完毕后开始执行异步代码,将保存下来的值拿出来使用,这时候 
//a = 0 + 10

console.log('script start')

async function async1() {
  await async2()
  console.log('async1 end')
}
async function async2() {
  console.log('async2 end')
}
async1()

setTimeout(function() {
  console.log('setTimeout')
}, 0)

new Promise(resolve => {
  console.log('Promise')
  resolve()
})
  .then(function() {
    console.log('promise1')
  })
  .then(function() {
    console.log('promise2')
  })

console.log('script end')
// script start => async2 end => Promise => script end 
// => promise1 => promise2 => async1 end => setTimeout

首先先来解释下上述代码的 async 和 await 的执行顺序。当我们调用 async1 函数时,会马上输出 async2 end,并且函数返回一个 Promise,接下来在遇到 await的时候会就让出线程开始执行 async1 外的代码,所以我们完全可以把 await 看成是让出线程的标志。

然后当同步代码全部执行完毕以后,就会去执行所有的异步代码,那么又会回到 await 的位置执行返回的 Promise 的 resolve 函数,这又会把 resolve 丢到微任务队列中,接下来去执行 then 中的回调,当两个 then 中的回调全部执行完毕以后,又会回到 await 的位置处理返回值,这时候你可以看成是 Promise.resolve(返回值).then(),然后 await 后的代码全部被包裹进了 then 的回调中,所以 console.log(‘async1 end’) 会优先执行于 setTimeout。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值