重学前端 17 # Promise里的代码为什么比setTimeout先执行?

简介: 重学前端 17 # Promise里的代码为什么比setTimeout先执行?

一、引言


一个 JavaScript 引擎会常驻于内存中,它等待着 宿主JavaScript 代码或者函数传递给它执行。


1、ES3版本以及之前,JavaScript 本身还没有异步执行代码的能力,宿主环境传递给 JavaScript 引擎,然后按顺序执行,由宿主发起任务。


2、ES5之后,JavaScript 引入了 Promise,不需要浏览器的安排,JavaScript 引擎本身也可以发起任务。


3、采纳JSC引擎术语,把宿主发起的任务称为宏观任务,把JavaScript引擎发起的任务称为微观任务。



二、宏观和微观任务


JavaScript 引擎等待宿主环境分配宏观任务,在 Node 术语中,把这个部分称为事件循环


1、用伪代码来表示:跑在独立线程中的循环

while(TRUE) {
    r = wait();
    execute(r);
}


2、整个循环做的事情基本上就是反复 等待 - 执行,这里的执行过程,其实都是一个宏观任务。可以大致理解为:宏观任务的队列就相当于时间循环。


3、在宏观任务中,JavaScript 的 Promise 还会产生异步代码,JavaScript 必须保证这些异步代码在一个宏观任务中完成,因此,每个宏观任务中又包含了一个微观任务队列:如下图所示


1141b001b0a586089f8bc09d1bd6bbae.jpg


例如:Promise 永远在队列尾部添加微观任务。setTimeout 等宿主 API,则会添加宏观任务。



三、Promise


   JavaScript 语言提供的一种标准化的异步管理方式,当进行 io、等待或者其它异步操作的函数,不返回真实结果,而返回一个承诺,函数的调用方可以在合适的时机,选择等待这个承诺兑现。


3.1、基本用法示例

 

function sleep(duration) {
        return new Promise(function(resolve, reject) {
            setTimeout(resolve,duration);
        })
    }
    sleep(1000).then( ()=> console.log("finished"));


Promisethen 回调是一个异步的执行过程。



3.2、Promise 函数中的执行顺序

    var r = new Promise(function(resolve, reject){
        console.log("a");
        resolve()
    });
    r.then(() => console.log("c"));
    console.log("b")
    // 输出顺序:a  b  c


3.3、setTimeout 混用的 Promise

    var r = new Promise(function(resolve, reject){
        console.log("a");
        resolve()
    });
    setTimeout(()=>console.log("d"), 0)
    r.then(() => console.log("c"));
    console.log("b")
    // 输出顺序:a  b  c  d


Promise 产生的是 JavaScript 引擎内部的微任务,而 setTimeout 是浏览器 API,它产生宏任务。所以d 必定在 c 之后输出。



3.4、一个耗时 1 秒的 Promise

    setTimeout(()=>console.log("d"), 0)
    var r = new Promise(function(resolve, reject){
        resolve()
    });
    r.then(() => {
        var begin = Date.now();
        while(Date.now() - begin < 1000);
        console.log("c1")
        new Promise(function(resolve, reject){
            resolve()
        }).then(() => console.log("c2"))
    });
    // 输出顺序:c1  c2  d


这个例子很好的解释了微任务优先的原理



3.5、如何分析异步执行的顺序

  • 1、首先我们分析有多少个宏任务
  • 2、在每个宏任务中,分析有多少个微任务
  • 3、根据调用次序,确定宏任务中的微任务执行次序
  • 4、根据宏任务的触发规则和调用次序,确定宏任务的执行次序
  • 5、确定整个顺序


    function sleep(duration) {
        return new Promise(function(resolve, reject) {
            console.log("b");
            setTimeout(resolve,duration);
        })
    }
    console.log("a");
    sleep(5000).then(()=>console.log("c"));
    // 输出顺序:a  b  c(c要等5秒)


第一个宏观任务中,包含了先后同步执行的 console.log("a"); 和 console.log("b");。

setTimeout 后,第二个宏观任务执行调用了 resolve,然后 then 中的代码异步得到执行,调用了 console.log("c")



四、新特性:async/await


async/awaitES2016 新加入的特性,它提供了用 for、if 等代码结构来编写异步的方式,并且运行时基础是 Promise


1、async 函数是在 function 关键字之前加上 async 关键字,这样就定义了一个 async 函数,可以在其中使用 await 来等待一个 Promise

function sleep(duration) {
    return new Promise(function(resolve, reject) {
        setTimeout(resolve,duration);
    })
}
async function foo(){
    console.log("a")
    await sleep(2000)
    console.log("b")
}
foo();
// 输出顺序:a  b(b要等两秒)


2、async 嵌套

function sleep(duration) {
    return new Promise(function(resolve, reject) {
        setTimeout(resolve,duration);
    })
}
async function foo(name){
    await sleep(2000)
    console.log(name)
}
async function foo2(){
    await foo("a");
    await foo("b");
}
foo2();
// 输出顺序:a(a等两秒) b(b也等两秒)
目录
相关文章
|
19天前
|
前端开发 JavaScript 开发者
水墨代码:前端川的诞生——在夏日阴雨中启航
【前端川】网站于农历五月初一(2024年6月6日)上线,融合水墨画与现代前端技术,呈现独特的水墨代码美学。创建者陈川分享技术心得与实战经验,网站特色包括水墨风格界面、技术深度解析及实战案例。在夏日雨中启航,"前端川"致力于为开发者提供灵感与帮助,探索前端技术的无限可能。
60 17
|
1月前
|
前端开发 JavaScript API
前端代码书写规范
前端代码规范提升项目可维护性和团队协作效率。关注项目命名清晰简洁、一致性,组件命名使用驼峰式且具描述性。JS遵循4空格缩进,分号结束语句,CSS按逻辑排序,HTML注重语义化。注释要功能性、文档化且简洁。遵循规范能减少错误,增强团队沟通。
94 3
|
8天前
|
前端开发 JavaScript 安全
高级前端开发需要知道的 25 个 JavaScript 单行代码
1. 不使用临时变量来交换变量的值 2. 对象解构,让数据访问更便捷 3. 浅克隆对象 4. 合并对象 5. 清理数组 6. 将 NodeList 转换为数组 7. 检查数组是否满足指定条件 8. 将文本复制到剪贴板 9. 删除数组重复项 10. 取两个数组的交集 11. 求数组元素的总和 12. 根据指定条件判断,是否给对象的属性赋值 13. 使用变量作为对象的键 14. 离线状态检查器 15. 离开页面弹出确认对话框 16. 对象数组,根据对象的某个key求对应值的总和 17. 将 url 问号后面的查询字符串转为对象 18. 将秒数转换为时间格式的字符串 19.
18 3
高级前端开发需要知道的 25 个 JavaScript 单行代码
|
2天前
|
前端开发 JavaScript Serverless
前端新手建议收藏的JavaScript 代码技巧
前端新手建议收藏的JavaScript 代码技巧
12 3
|
2天前
|
设计模式 前端开发 JavaScript
关于写好前端JS代码的一些建议
关于写好前端JS代码的一些建议
13 2
|
13天前
|
前端开发 JavaScript 安全
微前端架构采用 TypeScript 提升开发效率和代码可靠性
【6月更文挑战第12天】微前端架构采用 TypeScript 提升开发效率和代码可靠性。TypeScript 的类型安全防止了微前端间的类型错误,智能提示与自动补全加速开发,重构支持简化代码更新。通过定义公共接口和使用 TypeScript 编写微前端,确保通信一致性与代码质量。在构建流程中集成 TypeScript,保证构建正确性。总之,TypeScript 在微前端架构中扮演关键角色,推荐用于大型前端项目。
42 4
|
5天前
|
JavaScript 前端开发
杨校老师课堂之Web前端JS类库_JQuery案例[效果图与代码齐全]
杨校老师课堂之Web前端JS类库_JQuery案例[效果图与代码齐全]
12 0
|
1月前
|
前端开发 JavaScript
前端 js 经典:Promise
前端 js 经典:Promise
26 1
|
21天前
|
前端开发 JavaScript
综合案例(前端代码练习):猜数字和表白墙
综合案例(前端代码练习):猜数字和表白墙
15 0
|
1月前
|
JSON 前端开发 搜索推荐
BoostCompass( http_server 模块 | 项目前端代码 )
BoostCompass( http_server 模块 | 项目前端代码 )
38 4