• javascript学习笔记-Promise的基本用法


    javascript学习笔记-Promise的基本用法

    javascript,这门语言我接触好久了,但对它的了解一直是:用它写的代码我都看的懂,但真写起来就很别扭,要是扣细节就更不懂了。为什么呢?一方面这类动态语言在ide上的语法提示很弱,比不了java这种静态语言,没了提示就像屠夫没了刀,废了一半!另一方面毕竟不是主力语言,用到的地方能cv别人的代码绝不自己瞎折腾。最近在看一个nodejs写的开源项目,实现的功能挺有意思的,类似自动化工作流的东西,我一直对这方面的东西感兴趣,所以关注了,看了下有些细节看不明白,因此准备好好学习下JavaScript。

    期约(Promise)这个概念很抽象,简单说是为优化异步编程而生,本文也主要研究这个东西。

    js的异步编程

    js写的程序一般都是单线程跑的,如果没有异步的机制,所有逻辑都通过同步方式就会有很多问题,直观体现就是浏览器一直转圈圈卡着,基于这一点,js中大量用到异步编程。

    js中用异步编程的方式一般是这样的:

    function useAsyncDemo() {
        console.log('11111111111')
        setTimeout(() => {
            console.log('22222222222')
        }, 0)
        console.log('333333333333')
    }
    // --------输出-------
    //11111111111
    //333333333333
    //22222222222
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    js的执行引擎维护一个任务队列,在某个时机分配异步任务执行

    js的回调函数

    写过js代码的一般都知道回调函数这个概念,从字面意思就可以看出是:函数在某个时机将会被回调

    至于什么时机,我的理解是一般是某个异步任务执行完成后再调用这个函数。有了这种设计,即可实现直到异步函数执行结束再执行某个函数逻辑这种效果。

    举个例子:

    // 回调函数的例子
    // 有了回调函数,实现了直到异步返回结果后再执行回调函数的效果
    function useCallBackDemo() {
        function cb(res) {
            console.log('我是一个回调函数,我接收并打印一个参数:', res);
        }
    
        setTimeout(() => {
            let res = 100;
            console.log('异步计算了2s得到一个结果:', res);
            console.log('调用回调函数开始:', cb);
            cb(res);
        }, 2000);
    }
    // 输出
    //异步计算了2s得到一个结果: 100
    //调用回调函数开始: [Function: cb]
    //我是一个回调函数,我接收并打印一个参数: 100
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    get请求的实现一般是通过异步的方式,然后搭配回调函数可获得请求的结果。有一种场景是需要嵌套调用get请求的,如:

    1. 请求用户信息
    2. 基于1的用户id请求账户信息
    3. 基于2的账户信息请求余额信息

    要实现这种效果如果是基于回调函数的方式,不可避免地会形成回调嵌套,嵌套层数一多将形成回调地狱

    如:

    // 什么是回调地狱
    // 体现在层层嵌套
    function cbHellDemo() {
        // 模拟get操作,get操作一般耗时,异步执行
        // 接收一个回调函数的参数
        function get(cb) {
            setTimeout(() => {
                let res = Math.round(Math.random() * 10);
                console.log('1s后从服务端响应的数据:', res);
                cb(res);
            }, 1000)
        }
        get((res) => {
            console.log('我是cb1,我获得的结果是:', res);
            console.log('cb1还需继续发送get请求!');
            get((res) => {
                console.log('我是cb2,我获得的结果是:', res);
                console.log('cb2还需继续发送get请求!');
                get((res) => {
                    console.log('我是cb3,我获得的结果是:', res);
                })
            })
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    使用Promise优化异步编程

    我对Promise的基本理解是优化异步编程,先看Promise类型的基本用法:

    // Promise能够优化回调的操作
    // 先看看Promise的基本特性
    // 输出为1111->2222->3333
    // 说明Promise构造的执行器函数会被同步执行
    function basicPromiseDemo1() {
        console.log('111111111111')
        let p1 = new Promise(resolve => {
            console.log('2222222222222')
            resolve();
        })
        console.log('3333333333333')
    }
    
    // 输出为111111 33333333333 2222222222
    // Promise的执行器函数逻辑运行到resolve时,期约会落定
    // 落定后的期约会触发兑付或拒绝事件,体现在then方法中定义的相关事件被调用
    // 从结果看出即使resolve先于最后的console.log('333333')执行,其事件的触发逻辑还是较靠后执行
    // 原因是事件触发的执行逻辑是异步的
    function basicPromiseDemo2() {
        let p1 = new Promise(resolve => {
            console.log('1111111111');
            resolve('22222222222');
        });
        p1.then(value => {
            console.log(value);
        })
        console.log('33333333333333')
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    从上面的例子可以看出Promise的then方法中定义的兑付或拒绝事件会在期约落定(resolve)后被异步执行,且能够很容易的将值传递到后续事件中,如:resolve('22222222222'),将‘2222222222’传递到后续事件的入参中。这个特性简直是拯救回调地狱的绝佳方法!

    // 通过Promise很优雅实现:等到异步计算结果返回后再执行后续逻辑 这件事。
    function basicPromiseDemo3() {
        new Promise(resolve => {
            setTimeout(() => {
                let res = 100;
                console.log('异步计算了1s得到一个结果:', res);
                resolve(res);
            }, 1000)
        }).then(value => {
            console.log('从异步计算结束后接收到的值为:', value);
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    只要将异步操作包裹在Promise的执行器中,并在恰当时机resolve即可。

    解决回调地狱的方式:

    // 使用Promise解决回调地狱的问题!
    // 形式上体现为链式调用,无论是阅读还是编写都友好了很多
    // 一定程度上可以理解为异步转同步
    function basicPromiseDemo4() {
        function get() {
            return new Promise(resolve => {
                setTimeout(() => {
                    let res = Math.round(Math.random() * 10);
                    console.log('1s后从服务端响应的数据:', res);
                    resolve(res);
                }, 1000);
            })
        }
    	// then方法执行返回的值必然是Promise对象,如果返回的是普通的值,如'Ok'则直接是落定状态,但如果事件中显势的返回Promise对象则以返回的Promise对象为准
        get().then(value => {
            console.log('then1,从服务端接收到的数据是:', value);
            console.log('then1还需要继续请求服务端!');
            return get();
        }).then(value => {
            console.log('then2,从服务端接收到的数据是:', value);
            console.log('then2还需要继续请求服务端!');
            return get();
        }).then(value => {
            console.log('then3,从服务端接收到的数据是:', value);
            console.log('then3还需要继续请求服务端!');
            return get();
        }).then(value => {
            console.log('then4,从服务端接收到的数据是:', value);
            console.log('then4还需要继续请求服务端!');
            return get();
        }).then(value => {
            console.log('then5,从服务端接收到的数据是:', value);
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    使用async await的方式一定程度上使异步编程串行化,也可解决回调地狱那种问题:

    // 异步转同步的另一种方式:可通过async await的方式,此为es2018中新增的特性
    function asyncDemo() {
        function get() {
            return new Promise(resolve => {
                setTimeout(() => {
                    let res = Math.round(Math.random() * 10);
                    console.log('1s后从服务端响应的数据:', res);
                    resolve(res);
                }, 1000);
            })
        }
        (async () => {
            let res = undefined;
            res = await get();
            console.log(`当前时间${Date.now()},从服务端获取的数据是:${res}`);
            res = await get();
            console.log(`当前时间${Date.now()},从服务端获取的数据是:${res}`);
            res = await get();
            console.log(`当前时间${Date.now()},从服务端获取的数据是:${res}`);
            res = await get();
            console.log(`当前时间${Date.now()},从服务端获取的数据是:${res}`);
            res = await get();
            console.log(`当前时间${Date.now()},从服务端获取的数据是:${res}`);
        })()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    最后

    本人javascript的水平实在不高,本文仅作为自己学习过程的笔记记录,以便后续翻看和修改,如有理解上的问题请各位看官不吝赐教!

    学习过程编写的案例代码上传至github仓库:https://github.com/huanglusong/learn-javascript

  • 相关阅读:
    免费小程序HTTPS证书
    手写一个线程池
    Leetcode82删除排序链表中重复元素2
    深度学习基础课:课程介绍
    Linux--文件、进程、fork、open、系统调用、库函数相关知识
    关于报错java.util.ConcurrentModificationException: null的源码分析和解决
    项目管理PMP考试技巧
    day01(Flume)
    SpringBoot Web请求响应
    H5的基础
  • 原文地址:https://blog.csdn.net/qq_40748336/article/details/125465781