浏览器工作原理与实践
李兵
前盛大创新院高级研究员
立即订阅
6167 人已学习
课程目录
已完结 42 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 参透了浏览器的工作原理,你就能解决80%的前端难题
免费
宏观视角下的浏览器 (6讲)
01 | Chrome架构:仅仅打开了1个页面,为什么有4个进程?
02 | TCP协议:如何保证页面文件能被完整送达浏览器?
03 | HTTP请求流程:为什么很多站点第二次打开速度会很快?
04 | 导航流程:从输入URL到页面展示,这中间发生了什么?
05 | 渲染流程(上):HTML、CSS和JavaScript,是如何变成页面的?
06 | 渲染流程(下):HTML、CSS和JavaScript,是如何变成页面的?
浏览器中的JavaScript执行机制 (5讲)
07 | 变量提升:JavaScript代码是按顺序执行的吗?
08 | 调用栈:为什么JavaScript代码会出现栈溢出?
09 | 块级作用域:var缺陷以及为什么要引入let和const?
10 | 作用域链和闭包 :代码中出现相同的变量,JavaScript引擎是如何选择的?
11 | this:从JavaScript执行上下文的视角讲清楚this
V8工作原理 (3讲)
12 | 栈空间和堆空间:数据是如何存储的?
13 | 垃圾回收:垃圾数据是如何自动回收的?
14 | 编译器和解释器:V8是如何执行一段JavaScript代码的?
浏览器中的页面循环系统 (6讲)
15 | 消息队列和事件循环:页面是怎么“活”起来的?
16 | WebAPI:setTimeout是如何实现的?
17 | WebAPI:XMLHttpRequest是怎么实现的?
18 | 宏任务和微任务:不是所有任务都是一个待遇
19 | Promise:使用Promise,告别回调函数
20 | async/await:使用同步的方式去写异步代码
浏览器中的页面 (8讲)
21 | Chrome开发者工具:利用网络面板做性能分析
22 | DOM树:JavaScript是如何影响DOM树构建的?
23 | 渲染流水线:CSS如何影响首次加载时的白屏时间?
24 | 分层和合成机制:为什么CSS动画比JavaScript高效?
25 | 页面性能:如何系统地优化页面?
26 | 虚拟DOM:虚拟DOM和实际的DOM有何不同?
27 | 渐进式网页应用(PWA):它究竟解决了Web应用的哪些问题?
28 | WebComponent:像搭积木一样构建Web应用
浏览器中的网络 (3讲)
29 | HTTP/1:HTTP性能优化
30|HTTP/2:如何提升网络速度?
31|HTTP/3:甩掉TCP、TLS 的包袱,构建高效网络
浏览器安全 (5讲)
32 | 同源策略:为什么XMLHttpRequest不能跨域请求资源?
33 | 跨站脚本攻击(XSS):为什么Cookie中有HttpOnly属性?
34 | CSRF攻击:陌生链接不要随便点
35 | 安全沙箱:页面和系统之间的隔离墙
36 | HTTPS:让数据传输更安全
结束语 (1讲)
结束语 | 大道至简
课外加餐 (4讲)
加餐一|浏览上下文组:如何计算Chrome中渲染进程的个数?
加餐二|任务调度:有了setTimeOut,为什么还要使用rAF?
加餐三|加载阶段性能:使用Audits来优化Web性能
加餐四|页面性能工具:如何使用Performance?
浏览器工作原理与实践
登录|注册

19 | Promise:使用Promise,告别回调函数

李兵 2019-09-17
上一篇文章中我们聊到了微任务是如何工作的,并介绍了 MutationObserver 是如何利用微任务来权衡性能和效率的。今天我们就接着来聊聊微任务的另外一个应用 Promise,DOM/BOM API 中新加入的 API 大多数都是建立在 Promise 上的,而且新的前端框架也使用了大量的 Promise。可以这么说,Promise 已经成为现代前端的“水”和“电”,很是关键,所以深入学习 Promise 势在必行。
不过,Promise 的知识点有那么多,而我们只有一篇文章来介绍,那应该怎么讲解呢?具体讲解思路是怎样的呢?
如果你想要学习一门新技术,最好的方式是先了解这门技术是如何诞生的,以及它所解决的问题是什么。了解了这些后,你才能抓住这门技术的本质。所以本文我们就来重点聊聊 JavaScript 引入 Promise 的动机,以及解决问题的几个核心关键点。
要谈动机,我们一般都是先从问题切入,那么 Promise 到底解决了什么问题呢?在正式开始介绍之前,我想有必要明确下,Promise 解决的是异步编码风格的问题,而不是一些其他的问题,所以接下来我们聊的话题都是围绕编码风格展开的。

异步编程的问题:代码逻辑不连续

首先我们来回顾下 JavaScript 的异步编程模型,你应该已经非常熟悉页面的事件循环系统了,也知道页面中任务都是执行在主线程之上的,相对于页面来说,主线程就是它整个的世界,所以在执行一项耗时的任务时,比如下载网络文件任务、获取摄像头等设备信息任务,这些任务都会放到页面主线程之外的进程或者线程中去执行,这样就避免了耗时任务“霸占”页面主线程的情况。你可以结合下图来看看这个处理过程:
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《浏览器工作原理与实践》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(25)

  • Geek_Jamorx
    这三个题目非常重要,就跟做笔记一样回答了
    1、Promise 中为什么要引入微任务?

    由于promise采用.then延时绑定回调机制,而new Promise时又需要直接执行promise中的方法,即发生了先执行方法后添加回调的过程,此时需等待then方法绑定两个回调后才能继续执行方法回调,便可将回调添加到当前js调用栈中执行结束后的任务队列中,由于宏任务较多容易堵塞,则采用了微任务

    2、Promise 中是如何实现回调函数返回值穿透的?
    首先Promise的执行结果保存在promise的data变量中,然后是.then方法返回值为使用resolved或rejected回调方法新建的一个promise对象,即例如成功则返回new Promise(resolved),将前一个promise的data值赋给新建的promise

    3、Promise 出错后,是怎么通过“冒泡”传递给最后那个捕获
    promise内部有resolved_和rejected_变量保存成功和失败的回调,进入.then(resolved,rejected)时会判断rejected参数是否为函数,若是函数,错误时使用rejected处理错误;若不是,则错误时直接throw错误,一直传递到最后的捕获,若最后没有被捕获,则会报错。可通过监听unhandledrejection事件捕获未处理的promise错误



    2019-09-17
    3
    18
  • 皮皮大神
    老师,我觉得这章没有前面的讲得透彻,手写的bromise非常不完整,希望老师答疑的时候可以带我们写一遍完整promise源码,三种状态的切换,还有.then为什么可以连续调用,内部如何解决多层异步嵌套,我觉得都很值得讲解,老师带我们飞。

    作者回复: 这个加餐可以有!

    这篇问题主要在宏观视角建立对Promise的认知。

    关于手写 或者细节内容课程结束之后我们慢慢聊。

    目前被编辑追稿子压力大,好多问题没及时回复还望理解哈。课程结束之后我会相信回复大家的问题的。

    2019-09-18
    2
    7
  • Rapheal
    Promise的改进版,测试过也无问题。之前使用闭包存放所有回调函数有些问题,所有的Promise对象都是共享,这样会造成全局数据结构有问题。当前是基于回调函数数组传递在Promise对象之间传递实现。


        function _Promise(executor) {
            this._resolve = [];
            this._reject = [];
            this._catch;

            /*临时保存引用*/
            let self = this;
            
            this.then = function (resolve, reject) {
                resolve && this._resolve.push(resolve);
                reject && this._reject.push(reject);
                return this;
            }

            
            this.resolve = function (data) {
                setTimeout(() => {
                    let callback = self._resolve.shift();
                    self._reject && self._reject.shift();
                    let pro;
                    callback && (pro = callback(data));
                    self._resolve && (pro._resolve = self._resolve);
                    self._reject && (pro._reject = self._reject);
                    self._catch && (pro._catch = self._catch);

                }, 0)
            }

            this.reject = function (error) {
                setTimeout(() => {
                    let callback;
                    self._reject && (callback = self._reject.shift());
                    callback && callback(error);
                    callback || self._catch(error);

                }, 0);
            }

            this.catch = function (callback) {
                this._catch = callback;
                return this;
            }
            executor(this.resolve, this.reject);
        }


    function executor(resolve, reject) {
        let rand = Math.random();
        console.log(1)
        console.log(rand)
        if (rand > 0.5)
            resolve(rand)
        else
            reject(rand)
    }
    var p0 = new _Promise(executor);

    var p1 = p0.then((value) => {
        console.log("succeed-1")
        return new _Promise(executor)
    })

    var p3 = p1.then((value) => {
        console.log("succeed-2")
        return new _Promise(executor)
    })

    var p4 = p3.then((value) => {
        console.log("succeed-3")
        return new _Promise(executor)
    })

    p4.catch((error) => {
        console.log("error")
    })


    console.log(2)
    2019-10-05
    3
  • Angus
    看完这节之后我自己去实现了手写Promise,回顾了一下Promise,关于这方面的文章很多,我觉得老师大可不必在这里花大量篇幅去讲。专栏的名字是浏览器工作原理与实践,所以我希望老师能够更加着重这一方面的讲解。
    2019-09-20
    1
    2
  • 空间
    异步AJAX请求是宏任务吧?Promise是微任务,那么用Promise进行的异步Ajax调用时宏任务还是微任务?

    作者回复: ajax就是xmlhttprequest,必然是宏任务!

    准确地说,Promise在执行resolve或者reject时,触发微任务,所以在Promise的executor函数中调用xmlhttprequest会触发宏任务。

    如果xmlhttprequest请求成功了,通过resolve触发微任务

    如果xmlhttprequest请求失败了,通过reject触发微任务

    2019-09-18
    2
  • 胖虎
    "回调函数返回值穿透到最外层" 这句话配合老师您讲的例子是错误的 return x2 这个x2按照您那种方式没办法返回到最外层 最外层的x2和里面函数return 出来的x2根本就是两个东西
    2019-11-22
    1
  • Hurry
    这个太赞了 “ Promise 通过回调函数延迟绑定、回调函数返回值穿透和和错误“冒泡”技术 “, 之前看到别人手写实现 Promise,代码虽然可以看懂,但是理解不深,所以关键还是看如何实现这个三个点 回调函数延迟绑定、回调函数返回值穿透和和错误“冒泡”,结合这三个点和 promise API,手写一个 Promise, So easy

    ```js
    class PromiseSimple {
      constructor(executionFunction) {
        this.promiseChain = []; // 1.通过数组存储 callback,实现callback 延迟执行
        this.handleError = () => {};

        this.onResolve = this.onResolve.bind(this);
        this.onReject = this.onReject.bind(this);

        executionFunction(this.onResolve, this.onReject);
      }

      then(onResolve) {
        this.promiseChain.push(onResolve);

        return this;
      }

      catch(handleError) {
        this.handleError = handleError;

        return this;
      }

      onResolve(value) {
        let storedValue = value;

        try {
          this.promiseChain.forEach((nextFunction) => {
             storedValue = nextFunction(storedValue); // 2.循环,实现 callback 值传递
          });
        } catch (error) { // 3. try catch, 实现错误值冒泡
          this.promiseChain = [];

          this.onReject(error);
        }
      }

      onReject(error) {
        this.handleError(error);
      }
    }
    ```
    2019-09-20
    1
    1
  • Chao
    老师 你有答疑环节吗

    作者回复: 有,现在写稿子时间紧,等我主要稿件写完会抽出大把时间来专门解答问题。

    2019-09-17
    1
    1
  • 許敲敲
    面试手写promise也不怕了

    作者回复: 手写我加餐来讲

    2019-09-17
    1
  • 任振鹏
    老师 渲染流水线 在 微任务 之前 还是 之后 执行啊?

    作者回复: 微任务也可以不触发渲染操作,也可以出发渲染操作!

    比如你也可以在微任务中通过js来修改dom,触发重绘,重排等动作!

    你也可以在微任务中执行一些逻辑运算,这就和页面渲染没有关系了!

    2019-12-07
  • lisiur
    then函数为什么延时绑定就需要在微任务里执行resolve?先将结果保存下来,什么时候调用then函数时就把结果抛出去不行吗?
    2019-11-26
  • 昆虫捕手
    老师,文中用promise重构XFetch代码中,resolve接收了两个参数,第二个参数this的作用是什么,没看懂,希望老师解答下。
    2019-11-21
  • Geek_6b0898
    想问下老师什么时候给加餐课?支持老师尽快出啊

    作者回复: 快了,快了,最近家里小孩生病住院,折腾了好久,刚出院!

    2019-11-12
  • 凭实力写bug
    我比较想知道promise微任务的功能是怎么实现的
    2019-11-03
  • 蓝配鸡
    请问老师你用的什么工具画图?

    作者回复: mac下的keynote

    2019-11-01
  • Rapheal
    自己手写了一个_Promise,运行老师上面的程序,没啥毛病。除了使用宏任务,可以解决嵌套和参数穿透问题。


    var _Promise = (function () {
        
        /*使用闭包 保存所有的回调函数 */
        var _resolve = [];
        var _reject = [];
        var _catch;

        return function (executor) {

            this.print = function () {
                console.log(_resolve)
                console.log(_catch)

            }
            this.then = function (resolve, reject) {
                resolve && _resolve.push(resolve);
                reject && _reject.push(reject);
                return this;
            }

            this.resolve = function (data) {
                setTimeout(function () {
                    let callback = _resolve.shift();
                    let pro;
                    callback && (pro = callback(data));
                    _reject.shift();

                }, 0)
            }

            this.reject = function (error) {
                setTimeout(function () {
                    let callback = _reject.shift();
                    callback && callback(error);
                    callback || _catch(error);

                }, 0);
            }

            this.catch = function (callback) {
                _catch = callback;
                return this;
            }
            executor(this.resolve, this.reject);
        }
    })()

    function executor(resolve, reject) {
        let rand = Math.random();
        console.log(1)
        console.log(rand)
        if (rand > 0.5)
            resolve(rand)
        else
            reject(rand)
    }
    var p0 = new _Promise(executor);

    var p1 = p0.then((value) => {
        console.log("succeed-1")
        return new _Promise(executor)
    })

    var p3 = p1.then((value) => {
        console.log("succeed-2")
        return new _Promise(executor)
    })

    var p4 = p3.then((value) => {
        console.log("succeed-3")
        return new _Promise(executor)
    })

    p4.catch((error) => {
        console.log("error")
    })


    console.log(2)
    2019-10-05
  • tick
    Bromise实现的then是不是不太对,then是要有返回Promise的,我理解Promise对吧?
    2019-09-24
  • 柒月
    很棒,那个Bromise的代码绕了半天总算看明白了
    2019-09-20
  • 李懂
    问个问题,看了好多库发送一个请求new XMLHttpRequest(),咋不使用单例,发送请求共用一个或者请求对象池!
    2019-09-19
    1
  • 瞧,这个人
    老师会在加班课里给大家讲解典型课后思考题吗

    作者回复: 会的,可以把你的问题列出来,我答疑时会有针对性

    2019-09-19
收起评论
25
返回
顶部