闭包、promise、回调地狱及async/await

1、闭包

概述:闭包就是在函数内部返回一个函数,内部函数有外部函数的引用,这个结构称为闭包。

函数的生命周期:

1、预编译阶段:

  • 开辟一个人内存空间
  • 将对应的代码块放到这个内存空间

2、执行阶段:

  • 将对应的函数开辟的这个空间放在执行栈上
  • 执行栈就开始执行对应的函数对应的空间的代码块
  • 这个代码如果需要开辟空间,它就在这个对应的这个函数的内存空间上开辟
  • 当你不需要使用这个函数了,对应的函数的内存空间就会被回收,那么里面的代码开辟的空间也就被回收了
function fn(){
  var i = 0
  return function(){
    i++
    console.log(i)
  }
}
var f = fn()
f() //1
f() //2 

注意:如上述代码所示:如果我们需要保持i的状态,那么我们可以将这个i放到这个引用数据类型里面,然后保证这个引用数据类型对象的引用(对应的函数也是一个引用数据类型),这个时候gc就不会回收对应的这个i了。而这种保证i不被回收的机制就叫做闭包。(返回一个引用数据类型,这个里面保证对应i的引用,从而不被gc回收)

闭包的优点:
  • 内部函数拥有外部函数参数和变量的引用,使用我们的参数和变量的作用范围被扩大。
  • 对应的参数不被回收,在使用的时候就不需要重新开辟空间,速度更快。
  • 作为缓存。
闭包的缺点:
  • 内部函数要一直保持对外部函数里面参数和变量的引用。
  • 因为不会被回收所有对应的内存空间就会一直占用。

闭包的应用:

  • 防抖(在规定时间内只执行一次,执行最后一次)(一般应用于提交)

图例:
image.png
//第一个参数是做的操作  第二个参数是等待时间
function debounce(fn,delay){
    var timer = null;
    return function(){
        //清除上一次的等待
        clearTimeout(timer);
        //开始新的等待
        timer = setTimeout(fn, delay);
    }
}
  • 节流(在规定时间内执行第一次,减少执行次数)(一般应用于链接跳转)

图例:
image.png
//第一个参数是做的操作  第二个参数是执行一次的时长
function throttle(fn,delay) {
    var timer = null;
    return function () {
        //判断上一次是否走完
        if(timer) return
        //上一次走完开始下一次
        timer = setTimeout(()=>{
            fn();
            //走完了要将节流阀设置为false
            timer = null;
        },delay);
    }
}

防抖和节流的区别:

  1. 在规定的时间内:防抖执行最后一次,节流执行第一次。
  2. 在规定的时间内:防抖只会执行一次,节流是减少对应的执行次数。
  3. 防抖:开始下一次要先清除上一次;节流:开始下一次要先判断上一次是否执行完成。
  • 函数柯里化(将多个参数的函数拆分为多个单参数的函数,可以自由组合)
    核心:参数没够返回对应的函数,参数够了返回结果。
    简单的函数柯里化:
function sum(a){
    return function(b){
        return function(c){
            return a+b+c
        }
    }
}
console.log(`sum(1)(2)(3)`, sum(1)(2)(3));//6
console.log(`sum(1)(2)`, sum(1)(2));//fn

高阶函数柯里化:

//传递一个函数 (参数没到返回函数 参数到了返回结果)
function currying(fn) {
    //获取currying传递的参数
    let args = Array.prototype.slice.call(arguments,1)
    return function () {
        //将对应的函数的参数和curry传递参数做连接
        let arg = Array.from(arguments).concat(args)
        //判断参数个数是否一样
        if(arg.length < fn.length){
            //参数没到返回函数
            return currying.call(this,fn,...arg)
        }else{
            //参数到了 调用方法返回结果
            return fn.apply(this,arg)
        }
    }
}
//调用
function sum(a,b,c){
    return a+b+c
}
console.log(`sum(1,2,3)`, sum(1,2,3));
let fn = currying(sum)
console.log(`fn(2)`, fn(2));//函数
console.log(`fn(2)(3)`, fn(2)(3));//函数
console.log(`fn(2)(3)(1)`, fn(2)(3)(1));//6
console.log(` fn()()()(2)()()(3)()()()(1)`, fn()()()(2)()()(3)()()()(10));//15

2、promise

概述:promise是es6新增的一个类,这个类意为承诺。它被设计为异步的,它里面的内容是异步的(方法为异步的)。

promise的三种状态:
  • pening:等待状态(没有处理)
  • fulfilled:成功状态(有对应的处理)(里面resolve方法调用)
  • rejected:拒绝状态(有对应的处理)(里面代码报错或者里面reject方法调用)

    图例:
    image.png
构建promise对象:

使用new关键词new Promise()

  • 里面传递的参数是一个函数
  • 这个传递的函数里面有俩个参数,这个俩个参数也是一个函数
  • 这个函数里面的第一个参数为成功的函数(resolve),第二个参数为失败的函数(reject) (这个俩个函数都是异步的)
new Promise((成功的函数,失败的函数)=>{
    //包含异步的代码
    代码块
})
promise的方法:
1、原型方法
  • then 执行成功的回调(成功的回调,两个参数(成功的函数 失败的函数))
var promise = new Promise((resolve,reject)=>{
    //成功的函数调用 传递对应的参数
    resolve('成功')
})
//成功的回调 俩个参数 成功的函数 失败的函数
promise.then((res)=>{//res会接收resolve传递的参数
    console.log(`res`, res);
},(error)=>{//error 接收reject传递的参数
    console.log(`error`, error);
})
//.then默认请求下只有第一个有值,下面的都是undefined,如果下面的需要值,那么需要前面的return返回
promise.then((res)=>{
    console.log(`第一次then`, res);
    return 'hello'
}).then((res)=>{
    console.log(`第二次then`, res);
    return 'world'
}).then().then((res)=>{  //值穿透 当你的then没有处理它会给到下一个处理
    console.log(`第n次then`, res);
})
  • catch 执行失败(失败的回调,一个参数(传递为一个函数,这个函数可以接收rejected传递的参数))
//失败的回调 参数1个 传递为一个函数 这个函数可以接收rejected传递的参数
var promise = new Promise((resolve,reject)=>{
    reject('失败')
    // throw new Error('失败了')
})
//.catch默认请求下只执行第一个 如果需要走下面的 那么需要你报错
promise.catch((error)=>{
    console.log(`第一次`, error);
    throw new Error('失败了')
}).catch((error)=>{
    console.log(`第二次`, error);
    throw new Error('失败了')
}).catch((error)=>{
    console.log(`第三次`, error);
    throw new Error('失败了')
}).catch()
    .catch((error)=>{
    console.log(`第n次`, error);
})
  • finally 执行完成调用的
//promise  promise只能满足于一种状态 进入到成功它就成功了 进入失败就失败了
var success = new Promise((resolve,reject)=>{
    //成功的函数调用 传递对应的参数
    resolve('成功')
    reject('失败')
    // throw new Error('失败了')
})
//完成就能调用的函数 (成功  失败)
success.finally(()=>{
    console.log(`完成了`);
})
2、静态方法 (直接Promise.方法名调用)
  • resolve 方法(返回成功状态的promise)
//静态方法 Promise.方法名
//resolve方法
//返回一个成功的promise对象
var promise = Promise.resolve('hello')
console.log(`promise`, promise);
promise.then(res=>{
    console.log(res);
})
  • reject 方法(返回失败状态的promise)
// reject返回一个失败的promise对象
var promise1 = Promise.reject('错误')
console.log(`promise1`, promise1);
promise1.catch(error=>{
    console.log(error);
})
  • all 方法(并行执行所有的promise 如果遇到rejected就返回reject的promise 如果全部成功就返回所有的结果(promiseresult))
//all 传入一个promise数组 并行执行promise数组里面的promise (如果有一个是rejected 那么整体都是rejected)
var promise2 = Promise.resolve('hello')
var promise3 = Promise.reject('错误')
// var promise3 = Promise.resolve('成功')
var promise4 = Promise.resolve('world')
var promise5 = Promise.resolve('你好')
//all方法返回的是一个promise对象 如果全部成功对应的promiseResult里面的结果就是所有成功 否则就是错误的结果
var promise6 = Promise.all([promise2,promise3,promise4,promise5])
console.log(promise6);
  • allSettled 方法(互不影响执行对应的promise 返回所有的结果(状态一定是成功))
//传入一个promise数组 返回一个promise对象  不会互相影响 返回所有结果(状态为成功)
var promise7 = Promise.allSettled([promise2,promise3,promise4,promise5])
console.log(promise7);
  • race 方法(返回最快执行完成的promise)
//竞速 race  传入promise数组 返回最快走完的promise
var  promise8 =  Promise.race([promise2,promise3,promise4,promise5])
console.log(promise8);

promise的三种状态的流程图例:
QQ图片20221026210951.jpg

回调地狱:

概述:回调函数的无限嵌套导致当前代码失去了对应的维护价值和对应的可读性。
回调地狱示例:

//传入一个函数作为回调函数 在对应代码执行完成调用
function fn(fn) {
    setTimeout(function () {
        console.log('10');
        //走完了以后回调函数执行
        fn()
    }, 1000)
}
fn(() => {
    console.log(1);
})
//多个回调函数嵌套 回调地狱 (回调函数的无限嵌套 代码的可读性 可维护性 已经失去了)
fn(() => {
    console.log(1);
    fn(() => {
        console.log(2);
        fn(() => {
            console.log(3);
            fn(() => {
                console.log(4);
                fn(() => {
                    console.log(5);
                    fn(() => {
                        console.log(6);
                        ....
                    })
                })
            })
        })
    })
})
解决回调地狱的方法:
利用promise解决回调地狱(链式调用):

概述:在.then里面返回一个新的promise对象,在对应的异步代码执行完后调用resolve。

//利用promise来解决回调地狱的问题
new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(1);
        resolve()
    });
}).then(() => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(2);
            resolve()
        });
    })
}).then(()=>{
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(4);
            resolve()
        });
    })
}).then(()=>{
    console.log(5);
})

图例:
promise.gif
async await(es7新增的):

概述:aysnc和await是对应的两个连用的关键词,async是修饰函数的,await是修饰promise对象的。

  • await只能在async内使用
  • async修饰的函数返回一个promise对象,
  • await修饰的promise对象会占用当前的线程,直到对应的promise执行完成才会释放。
async function fn() {
    await new Promise((resolve,reject)=>{ //如果没有放行后面的不会执行
        setTimeout(() => {
            console.log('hello');
            resolve()
        })
    })
    console.log('world');
}
//async修饰完函数执行会返回一个promise对象
console.log(fn());
//async修饰的函数返回的promise对象 
//里面的报错 会使当前的promise对象的状态为rejected 
//如果里面return内容那么内容将会传递给对应的then
async function fn1(){
    throw new Error('错误')
    // return '我是fn1'
}
fn1().then(res=>{
    console.log(res);
},error=>{
    console.log(error);
})
//await会使用当前的函数的线程占用 直到对应的修饰的promise执行完成
// await Promise.reject() 报错
利用async和await来解决回调地狱:
function fn(v,delay) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(v);
            resolve()
        },delay);
    })
}
async function fn1(){
    await fn(1,1000)
    await fn(2,2000)
    await fn(3,100)
    await fn(4,0)
    console.log(5);
}
fn1()

图例:
image.png
总结:
  • async修饰函数的
  • await修饰promise对象
  • async里面使用await,如果这个await修饰的promise没有执行完,那么对应的async修饰的函数返回的promise的状态是pening(等待状态)。
  • 如果async修饰的函数里面什么都没有,那么对应返回的promise状态是成功(默认函数返回undefined)
  • async修饰的函数,返回值就是成功,返回的值传递给then方法
  • async修饰的函数如果里面报错,那么返回的是失败,传递的值为报的错
  • await只能在async里面使用 ,await会使当前的函数陷入等待

代码执行机制:

  • 同步代码执行比异步代码块
  • 同步代码的执行是利用对应的js引擎解析的
  • 异步代码执行是利用事件轮询机制执行的
事件轮询机制:
  • 先找script标签里面的微任务
  • 按照微任务队列执行完对应的微任务
  • 进入下一个宏任务 执行对应的宏任务代码
  • 进行宏任务对应微任务队列 执行对应微任务
  • 再进行到下一个宏任务 执行对应的微任务
  • 直到对应的宏任务队列和微任务队列被清空
宏任务:

script 、 定时器(setInterval setTimeout)、 事件...

微任务:

promise.then、promise.catch、nextTick ....

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容