资讯专栏INFORMATION COLUMN

Promise学习总结

twohappy / 517人阅读

摘要:引擎线程也称为内核,负责处理脚本程序例如引擎引擎线程负责解析脚本,运行代码。对象代表一个未完成但预计将来会完成的操作。注意一旦新建就会立即执行它属于,无法取消。

写在前面:
第一遍学Promise时, 只是大概过了一遍, 感觉学的不够深入, 这一篇算是对之前的一个总结吧. Promise在ES6中也属于一个较难理解的一部分; 所以在学习一个比较难理解的知识点时, 我们可以围绕这个知识点进行展开,逐个去理解.

再探Promise

理解一个知识点, 不妨先列出下面几个问题.

Promise是用来干什么的?

Promise是什么?

Promise如何去创建,使用?

Promise的常用形式?

Promise的使用有哪些注意点?

异步相关背景介绍 浏览器内核

首先聊一下浏览器, 一直对浏览器的结构比较好奇,查了很多资料总结就有下面一点相关总结; 其中也借鉴其他人的一些东西.
浏览器是多进程的,有主进程, GPU加速进程,渲染进程(内核)等, 一般新开一个tab页面就是新启动一个进程, CPU就会给他分配资源; 但其中有一个核心进程==>渲染进程(浏览器内核), 是我们前端人员需要特别关注的,它包括了多个线程...

GUI渲染线程

负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行

注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),
GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。

JS引擎线程

也称为JS内核,负责处理Javascript脚本程序.(例如V8引擎)JS引擎线程负责解析Javascript脚本,运行代码。
JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。

不过H5中新增了Web Worker, 实现了多线程: js会新开线程来处理一些其他任务,但不会影响DOM结构...
创建Worker时,JS引擎向浏览器申请开一个子线程(子线程是浏览器开的,完全受主线程控制,而且不能操作DOM)
JS引擎线程与worker线程间通过特定的方式通信
(postMessage API,需要通过序列化对象来与线程交互特定的数据)

事件触发线程

这个线程是归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求,
页面滚动等),会将对应任务添加到事件线程中
当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
注意,
由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)

定时触发器线程

即setInterval与setTimeout所在线程
浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确); 
因此通过多带带线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
当然setTimeout中的延时参数也不一定准确

异步HTTP请求线程

在XMLHttpRequest在连接后是通过浏览器新开一个网络线程去请求
将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,
将这个回调再放入事件队列中。再由JavaScript引擎执行。

那么关于浏览器方面的背景知识就介绍到这里啦, 想要深入去了解,可以去查相关资料...

事件队列和循环

大家都知道JavaScript引擎是单线程的工作模式, 即同一时间只能跑一段代码,还要按顺序自上而下执行; 但是碰到I/O操作, 定时器, 事件监听函数等这些耗时操作; JS引擎不会等待它们有结果了才去之下它们后面的代码, 而是会将它们扔进任务(事件)队列中, 等待同步代码执行栈空了之后, 再去任务队列将任务一个个取出来执行任务所对应的回调函数, 执行完毕后会一直等待新的任务到来; 如此循环...

几个类型的回调

同步回调函数

我们可以利用了函数的执行栈顺序,函数作为参数放到另一个函数中调用, 谁在后面调用谁就先被放在函数执行栈栈顶

异步回调函数

事先在外面定义好一个callback; 将回调函数作为某个函数的参数, 利用函数的作用域将函数中异步任务得到的结果存在回调函数的形参中, 然后在函数体末尾调用...

定时器
setTimeout的作用是在间隔一定的时间后,将回调函数插入任务队列中,等栈中的同步任务都执行完毕后,再执行, 当然这个时间不一定准确...

Promise是用来干什么的?

看阮老师的ES6出门上说Promise是JS异步编程的一种解决方案. 举个例子, Ajax的回调问题, 如果下一个ajax请求要用到上一个Ajax请求中的结果, 那么往往就会导致多个回调嵌套的问题, 那么Promise就可以解决这种代码上的嵌套问题, 是我们的代码变得更优美, 更利于维护; 我暂时先对Promise的理解就是: 处理异步任务, 保存异步结果状态, 异步代码同步化...

Promise是什么?

Promise 它就是一个对象,相当于一个容器, 里面存的就是一个异步操作的结果; 我们可以是从中获取异步操作结果的相关信息。

Promise对象代表一个未完成、但预计将来会完成的操作。
它有以下三种状态:

pending:初始值,不是fulfilled,也不是rejected
fulfilled:代表操作成功
rejected:代表操作失败

Promise有两种状态改变的方式,既可以从pending转变为fulfilled,也可以从pending转变为rejected。一旦状态改变,就「凝固」了,会一直保持这个状态,不会再发生变化。当状态发生变化,promise.then绑定的函数就会被调用。
注意:Promise一旦新建就会「立即执行」(它属于microtask),无法取消。这也是它的缺点之一。

Promise的创建和使用?

1.创建promise对象

//1.使用new Promise(func)的形式
//2.快捷语法: Promise.resolve(func) || Promise.reject(func)
// 参数1: 一般是一个处理异步任务的函数  
// 返回值: 一个promise实例对象
Promise.resolve("foo")
// 等价于, 不过参数类型不一样执行的操作也会有所不同
new Promise(resolve => resolve("foo"))

2.在函数func中 放异步处理代码

// 传入两个参数: 回调函数resolve, reject分别去保存异步处理的结果
// 成功: 使用resolve(结果)
// 失败: 使用reject(原因) 

3.调用实例的then(func1) 或者 catch(err)

首先then方法是异步执行, 对上面的异步结果进行处理的函数
参数: 传回调函数, 一个两个都行, 前者是成功状态的回调,后者是失败的回调
Promise常用的场景?

promise一般的使用套路就是:

1.先定义一个函数, 函数内部使用new Promise()的方式来返回一个promise对象, resolve用来保存 异步处理成功的结果
reject用来保存 异常处理的结果
2.然后函数调用,传参
3.链式语法点出then方法, then中的回调用来处理异步结果
4.有错误就点出catch方法, 也可以用then(null, function() {})代替catch
5.then的回调中也可return一个值, 会被包装成一个新的promise, 因此可以继续调用then方法

应用场景: 在ajax中使用, 解决异步嵌套问题

    function ajax(url) {

        return new Promise((resolve, reject) => {
    
            let xhr = new XMLHttpRequest();
            // 请求类型, 地址, 异步
            xhr.open("get", url, true);
            xhr.send();
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    try {
                        // 处理响应内容, 将内容丢到成功状态的回调
                        resolve(JSON.parse(xhr.responseText))
                    } catch (e) {
                        // 捕获错误, 丢到失败状态的回调
                        reject(e)
                    }
                }
            }
        });
    }

    // 调用 封装的ajax函数
    let url = "http://127.0.0.1:3000/xxoo"; // 自己本地开的一个服务

    ajax(url)
        .then(res => console.log(res)) // 输出 {code: 0, msg: "hello cors"}
        .catch(err => console.log(err))

```

其他场景

    // 实现串行任务管道; 即当前任务的输出可以作为下一个任务的输入,形成一条数据管道;
    // 比如: 比如从url1获取参数userId,拿到后再从url2获取第三方openId,最后再从url3货取orderList,
    然后把结果展示给用户,类似的逻辑都是任务管道:
    
    new Promise(function(resolve, reject) {
        resolve(1);
    })
    .then(function(res) {
        return new Promise(function(resolve, reject) {
            resolve(res + 1);
        });
    })
    .then(function(res) {
        return new Promise(function(resolve, reject) {
            resolve(res + 1);
        });
    })
        .then(function(res) {
        console.log(res); // 3
    });

promise的好处

在异步执行的流程中,使用Promise可以把 执行代码 和 处理结果 的代码清晰地分离
这样我们便可以 把执行代码 和 结果处理 分成不同的模块来写,易于维护

减少异步回调的嵌套, 比如ajax回调, 我们可以依次调用then方法即可, 还可以控制回调的顺序

多个异步任务是为了容错去访问用同一资源时, 可以使用Promise.race([promise实例...])

多个异步任务并行执行时,比如ajax访问两个接口, 可以用Promise.all([promise实例...])

Promise使用的注意事项

Promise构造函数内的同步代码立即执行

回调函数参数resolve异步执行, 将结果作为参数传给then方法中的回调函数

resolve只有第一次执行有效,状态不能二次改变

then和catch如果有return, 返回的是一个全新的promise对象, 可以链式调用

Promise构造函数只会执行一次, promise实例会保存resolve的状态,
以后这个实例每次调用then都是返回一个这个状态, 若链式调用then,下一个则会打印undefined, res没有值...

then中返回任意一个非 promise 的值都会被包裹成 promise 对象

.then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环

.then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。

.then 可以接收两个参数,第一个是处理成功的函数,第二个是处理错误的函数。.catch 是 .then 第二个参数的简便写法,但是它们用法上有一点需要注意:.then 的第二个处理错误的函数捕获不了第一个处理成功的函数抛出的错误,而后续的 .catch 可以捕获之前的错误。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102596.html

相关文章

  • promise以及async、await学习总结

    Promise/async、await帮我们解决了什么 它给我们提供了一种新的异步编程解决方案,同时避免了困扰已久的回调地狱 // 异步的处理可能会产生这样的回调地狱(第二个异步操作和第一个异步的结果有关系) let Ajax = function(data, success, error){ $.ajax({ data: data, success: function...

    zero 评论0 收藏0
  • promise以及async、await学习总结

    Promise/async、await帮我们解决了什么 它给我们提供了一种新的异步编程解决方案,同时避免了困扰已久的回调地狱 // 异步的处理可能会产生这样的回调地狱(第二个异步操作和第一个异步的结果有关系) let Ajax = function(data, success, error){ $.ajax({ data: data, success: function...

    mist14 评论0 收藏0
  • promise以及async、await学习总结

    Promise/async、await帮我们解决了什么 它给我们提供了一种新的异步编程解决方案,同时避免了困扰已久的回调地狱 // 异步的处理可能会产生这样的回调地狱(第二个异步操作和第一个异步的结果有关系) let Ajax = function(data, success, error){ $.ajax({ data: data, success: function...

    fuchenxuan 评论0 收藏0
  • ES6-7

    摘要:的翻译文档由的维护很多人说,阮老师已经有一本关于的书了入门,觉得看看这本书就足够了。前端的异步解决方案之和异步编程模式在前端开发过程中,显得越来越重要。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。 JavaScript Promise 迷你书(中文版) 超详细介绍promise的gitbook,看完再不会promise...... 本书的目的是以目前还在制定中的ECMASc...

    mudiyouyou 评论0 收藏0
  • Promise学习总结与思考

    摘要:学习地址详见正文调用或并不会终结的参数函数的执行调用以后,后面的还是会执行,并且会首先打印出来。这是因为立即的是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。另外,方法指定的回调函数,如果运行中抛出错误,也会被方法捕获。 学习地址详见:http://es6.ruanyifeng.com/#do... 正文 1.调用resolve或reject并不会终结 Promise 的参数函...

    canopus4u 评论0 收藏0

发表评论

0条评论

twohappy

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<