聊一聊 Promise中then方法的各种骚操作

Posted summer·

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了聊一聊 Promise中then方法的各种骚操作相关的知识,希望对你有一定的参考价值。

先说一下微任务:

微任务队列:

异步任务需要适当的管理。为此,ECMA 标准规定了一个内部队列 PromiseJobs,通常被称为“微任务队列(microtask queue)”(ES8 术语)

  • 队列(queue)是先进先出的:首先进入队列的任务会首先运行。

  • 只有在 javascript 引擎中没有其它任务在运行时,才开始执行任务队列中的任务。

    https://zh.javascript.info/microtask-queue

“任务队列"是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列”,就是读取里面有哪些事件。https://www.ruanyifeng.com/blog/2014/10/event-loop.html

​ ——阮一峰

我的理解:微任务队列其实就是一些事件的傀儡队列,这个傀儡就是回调函数,当主线程任务完成之后,开始读取微任务队列中的事件时,就是去执行事件对应的回调函数。

ps:(可以想象成一个工作狂,他会提前准备一个框框(微任务队列)放一些当前任务中可以慢慢处理的其他任务,然后先去做他当前的主要的工作,做完主要工作就回去检查这个框框,这个框的任务也做完了,就去问老大(宏任务队列)还有没有下一个任务(下一个宏任务))

微任务中也可以有微任务,所以会有无限套娃的风险。https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_DOM_API/Microtask_guide

promise中的then方法:

  1. 明确then的回调函数是什么时候进入微任务队列的

思否:https://segmentfault.com/q/1010000022578087
我觉得这个答案更符合我的理解:
then的回调函数什么时候进入PromiseJobs取决于then方法前一个promise的状态,
如果调用 then 时 promise 是 pending 状态,回调会进入 promise 的 [[PromiseFulfill/RejectReactions]] 列表里;否则会进入 PromiseJobs。

看例子:

eg1(调用then的promise状态为pending):
new Promise(()=>).then((res)=>console.log("我是then"+res))
console.log("我是同步的")
输出:
"我是同步的"
结论:如果调用 then 时 promise 是 pending 状态,回调会进入 promise 的[[PromiseFulfill/RejectReactions]] 列表里等待执行;
eg2(调用then的promise状态不为pending):
new Promise((res)=>res(1)).then((res)=>console.log("我是then:"+res))
console.log("我是同步的")
输出:
"我是同步的"
"我是then:1"
结论:
如果调用 then 时 promise 不是 pending 状态,则会进入 PromiseJobs,待同步任务执行完,则执行PromiseJobs中的任务
eg3:
new Promise((res)=>).then(console.log("我是then"))
console.log("我是同步的")
输出:
"我是then"
"我是同步的"
思考:调用then的promise是pending状态,为什么"我是then"还是会打印?👇
eg4:
new Promise((res)=>res(1)).then(console.log("我是then"))
console.log("我是同步的")
输出:
"我是then"
"我是同步的"
思考:????为什么还是会打印👇

👇👇👇

  1. then方法的参数

    then方法接受两个参数(onFulfilled, onRejected)

    1. If onFulfilled is not a function, it must be ignored.

    2. If onRejected is not a function, it must be ignored.

      ​ 文档——https://promisesaplus.com/

    上面说的很清楚,如果接受的参数不是一个函数,那么必须被忽略(不管他)所以会有值穿透的情况

    好,再来看看上面的思考:

    new Promise(()=>).then(console.log("我是then"))
    console.log("我是同步的")
    
    首先明确一点:console.log是一个方法,也就是一个函数,所以你再看看你then里面传的是什么,是console.log("xxxxxx"),所以你在这里同步立即执行这个函数,然后把这个函数的返回值传到then里面去,所以相当于你在then里面传了一个值undefined
    
    不信,我们来看看:
    new Promise((res)=>res(1)).then(console.log("我是then"))
    console.log("我是同步的")
    这个输出和上面一样,但是不一样的是,调用then的promise有结果  1 ,且状态不是pending
    所以就算中间的then会被忽略,再调用一个正常then还是可以接受到数据
    
    new Promise((res)=>res(1))
        .then(console.log("我是then"))
        .then(res=>console.log("promise的结果:"+res))
    console.log("我是同步的")
    输出:
    "我是then"
    "我是同步的"
    "promise的结果:1"
    
    不信再来:
    new Promise(()=>)
        .then((function()console.log(2))())//中间这个then传入一个立即执行的函数效果和上面一样
        .then(res=>console.log(res))
    console.log("我是同步的")
    输出:
    2
    "我是同步的"
    
    好了,到了这里应该都明白了,接下来,看一下then添加回调函数的写法:https://wangdoc.com/javascript/async/promise.html#promiseprototypethen
    
    let func = function() 
        return new Promise((resolve, reject) => 
            resolve('我是老大new Promise');
        );
    ;
    
    let cb = function() 
        return '我是回调函数的返回值';
    
    // 1. 
    func().then(function () 
        return cb();//这一句等同于 return '我是回调函数的返回值'
    ).then(resp => 
        console.warn(resp);  // 输出 '我是回调函数的返回值'
        console.warn('1 =========<');
    );
    // 2. 
    func().then(function () 
        cb();//在这个函数里面只是执行了一下cb函数,但是没有返回任何东西:所以默认返回undefined
        return undefined
    ).then(resp => 
        console.warn(resp);// 输出: undefined
        console.warn('2 =========<');
    );
    // 3. 
    func()
        .then(cb())// 这个then里面传入的相当于是cb函数的返回值 '我是回调函数的返回值',所以发生值穿透
        .then(resp => 
            console.warn(resp); // 值穿透 ——>  输出:'我是老大new Promise'
            console.warn('3 =========<');
    );
    // 4. 
    func().then(cb)// 这里的then里面传入的是一个函数,而不是一个值,所以内部会对这个函数进行一个封装,返回'我是回调函数的返回值'
        .then(resp => 
            console.warn(resp); //输出:'我是回调函数的返回值'
            console.warn('4 =========<');
    );
    内部封装大致如下:👇👇
    
    .then(new Promise((res,rej)=>
            res(cb());
        ))
    

console.log()和console.log是不一样的

then方法内部会进行一个判断:看是否是函数

  1. 当then里面传入console.log时,相当于传入了一个函数log,于是promise的函数执行器就会帮你调用new Promise(),去调用这个函数,

    传入console.log时,在then中相当于发生如下过程
    new Promise(res=>res(1)).then(()=>
    		return new Promise((res,rej)=>
                res(console.log());
            )
    	)
    
  2. 当then里面传入console.log()时,相当于传入了一个非函数,也就是console.log()这个函数的返回值undefined,所以会发生值穿透,所以上面第二种情况,先打印2,再打印new Promise中的res的参数值

  1. then的回调方法的注册时机(事件机制是先注册再执行)

注册时机其实还是看调用then的promise状态,只要promise状态改变了,其调用的then的方法就会被注册,但是还没有被执,执行再按照注册顺序进行

https://juejin.cn/post/6844903987183894535

以上是关于聊一聊 Promise中then方法的各种骚操作的主要内容,如果未能解决你的问题,请参考以下文章

聊一聊promise的前世今生

聊一聊模板方法模式

聊一聊幂等

聊一聊线程是如何运行的

简单聊一聊Ansible自动化运维

简单聊一聊Ansible自动化运维