zoukankan      html  css  js  c++  java
  • Promise

    什么是Promise

    Promise中文意为"诺言",现在对它的历史解析开始了。
    JavaScript是单线程的,调用函数执行一个异步操作的时候,通常会塞给这个函数几个callback回调函数,这个函数在未来自主选择在何时调用、调用哪个、传递什么参数。
    那如果你不先确定好要如何编写这个callback(比如你还没想清楚要如何使用回调时的参数),你就无法调用这个函数,直到你想清楚了,写好了,你才能调用它,任务才会开始。
    一旦你不在callback中作出决定,就调用了这个函数,那么你将永远失去你可以获得的result。

    我们用同步来模拟异步,看看其中有什么乾坤

    const child_process = require('child_process');
    
    // 模拟一次耗时2s的操作, 得到结果8
    function await(millis) {
            child_process.execSync(`sleep ${ millis / 1000 };`);
            return 8;
    }
    
    function awaitResult(callback) {
            console.log('开始异步任务');
            var result = await(2000);
            callback(result);
            console.log('完成异步任务');
    }
    
    console.log('开始调用异步函数');
    awaitResult(result => {
            console.log('结果是' + result);
    });
    console.log('完成调用异步函数');
    
    /**
    开始调用异步函数
    开始异步任务
    (...2s后)
    结果是8
    完成异步任务
    完成调用异步函数
    */
    

    而我们改用Promise来重写它以后:

    const child_process = require('child_process');
    
    // 模拟一次耗时2s的操作, 得到结果8
    function await(millis) {
            child_process.execSync(`sleep ${ millis / 1000 };`);
            return 8;
    }
    
    function awaitResult() {
            var task = new Promise((resolve, reject) => {
                    console.log('开始异步任务'); // Promise构造函数会立即执行这个函数
                    var result = await(2000);
                    resolve(result);
            });     
            console.log('完成异步任务');
            return task;
    }
    
    console.log('开始调用异步函数');
    var task = awaitResult();
    console.log('完成调用异步函数');
    task.then(result => {
            console.log('结果是' + result);
    });     
    
    /**
    开始调用异步函数
    开始异步任务
    (...2s后)
    完成异步任务
    完成调用异步函数
    结果是8
    */
    

    相信大家一定可以看出来了,"诺言"的含义:
    立刻执行这个任务!成功的话我等一下会告诉你你接下来该怎么办的,把结果给我留好,随时待命。对了,失败的话我也会告诉你该怎么办的,你先做,做完好好等着就是了,说不定你还没做完我就已经告诉你该怎么弄了。

    调用者(程序员)承诺:会告诉执行者如何处理result。(! 这个操作是可以重复的)
    执行者(Promise)承诺:会在未来某个时机执行调用者给出的函数,当然了,人家最爱的是result,传递给他。

    Promise 的承诺

    Promise是在何时承诺的呢?关键就在于下面这行代码,resolvereject就是Promise给我们的承诺之星,分别对应成功和失败两种状态,用then()catch()来回应。

    resolve(result);
    

    Promise 的状态

    Promise 对象通过自身的状态,来控制异步操作。Promise 实例具有三种状态。

    • 异步操作未完成(pending)
    • 异步操作成功(fulfilled)
    • 异步操作失败(rejected)

    上面三种状态里面,fulfilled和rejected合在一起称为resolved(已定型)。

    这三种的状态的变化途径只有两种。

    • 从“未完成”到“成功”
    • 从“未完成”到“失败”

    一旦状态发生变化,就凝固了,不会再有新的状态变化。

    这也是 Promise 这个名字的由来,它的英语意思是“承诺”,一旦承诺成效,就不得再改变了。这也意味着,Promise 实例的状态变化只可能发生一次。

    当然了,对于这种说法本人不置可否。就连MDN英文也表示:JavaScript中的承诺表示已经发生的进程,可以与回调函数链接在一起。

    因此,Promise 的最终结果只有两种。

    • 异步操作成功,Promise 实例传回一个值(value),状态变为fulfilled。
    • 异步操作失败,Promise 实例抛出一个错误(error),状态变为rejected。

    Promise 对象、Promise 对象状态、承诺

    现在我们再次强化这种认知:
    一个Promise对象被new后立即执行构造时用户传递的函数,这个函数的原型为:(resolve, reject) => {},在调用resolve()reject()之前,Promise处于pending状态,直到有且仅有其中一个状态函数被调用,Promise对象状态被改变,成为resolved状态,及fulfilled或rejected状态之一。
    resolve(...param)导致Promise进入fulfilled状态,对Promise # then((...param) => {})的每次调用都将得到承诺被回调,并且传递对应的param。成功状态下的Promise # catch()调用就像被忽略了一样。
    reject(...param)导致Promise进入rejected状态,对Promise # catch((...param) => {})的每次调用都将得到承诺被回调,并且传递对应的param。then()的处理请看链式调用。

    链式调用

    容易迷糊的地方来了! then()和catch()也返回新的Promise对象!
    我们需要研究then、catch的机制。

    我们先看看一个链式调用的例子:

    "use strict";
    
    function a(state) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                if (state)
                    resolve('Task A succeed');
                reject('Task A failed');
            }, 800);
        });
    }
    
    function b(state) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                if (state)
                    resolve('Task B succeed');
                reject('Task B failed');
            }, 800);
        });
    }
    
    {
        // a进入成功状态,then参数r=>{}被执行,then返回的Promis对象也进入成功状态
        a(true).then(r => {
            console.log('[A]', r); // 800ms后输出:[A] task A succeed
        });
    }
    
    {
        // a进入成功状态,catch参数r=>{}不被执行,catch返回的Promise对象直接进入成功状态
        a(true).catch(r => {
            console.log('[B]', r); // 不会打印
        }).then(r => {
            console.log('[B2]', r);
        });
    }
    
    {
        // a进入成功状态,该对象没有then方法,没有任何处理
        a(true);
        // a进入失败状态,该对象没有一个catch方法,抛出UnhandledPromiseRejectionWarning异常,不是这个调用抛出的,是Promise内部抛出的
        // a(false); // 会导致异常
        // 给会失败的a一个catch处理方法,UnhandledPromiseRejectionWarning异常消失
        a(false).catch(e => {
            console.log('[C]', e);
        });
        // 在a进入失败状态之前就应该设置catch方法,否则照样报异常
        if (false) {
            let tmp = a(false);
            setTimeout(() => {
                tmp.catch(r => {
                    console.log('[C2]', r);
                });
            }, 1000);
        }
        // 失败状态的Promise不会回调then处理方法,不过then返回的Promise对象也进入失败状态,由于父子Promise都没有catch,抛出异常
        if (false)
        a(false).then(r => {
            console.log('[C3]');
        });
        // then进入失败状态后调用catch处理方法,异常得到了处理,父Promise不抛出异常
        a(false).then(r => {
            console.log('[C4]');
        }).catch(e => {});
        // 失败的传递性
        if (false) {
            let tmp = a(false);
            tmp.catch(() => {console.log('失败1');}); // 执行
            tmp.then(() => {console.log('失败3');}).catch(() => {console.log('失败4');}); // tmp失败,但是异常传递给then并得到了catch处理
            tmp.then(() => {console.log('失败2');}); // then失败没有catch,抛出异常
        }
    }
    
    {
        // a成功,执行then,由于then处理方法返回一个Promise,then仍然处于pending状态,直到b失败,then失败,下一个then失败,执行catch
        a(true).then(r => b(false)).then(r => {
            console.log('[D]', r);
        }).catch(reason => {
            console.log('失败原因:', reason);
        });
    }
    
    • Promise1.then(r => use(r)).then(r => use(r)).catch(reason => use(reason));
      对于Promise1对象的then调用,当Promise状态定型时,若
      1.失败,则then返回的Promise2失败,...失败原因传递给最后一个then,如果没有被catch则抛出异常
      2.成功,执行then处理方法,如果use(r)返回Promise对象,则等待该对象,这里发生了对象的代理,否则then成功,执行下一个then。

    ES6 async await 关键字

    有了Promise还不够,我们可以使用async关键字将一个function函数或者箭头函数声明为同步函数,对于一个返回Promise实例的函数调用,使用await关键字获取resolve()函数传递的值,仿佛编写同步代码一样,无需回调函数。

    "use strict";
    
    function getData() {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(888);
            }, 800);
        });
    }
    
    async function test() {
        try {
            var result = await getData();
            console.log('computed result:', result);
        } catch(e) {
            console.log('An Error occurred');
        }
    }
    
    (function main() {
        test();
        (async () => {
            var n = await getData();
            console.log('computed result:', n);
        })();
    })();
    

    直接await new Promise();也是可以的!

        (async () => {
            var n = await new Promise((resolve, reject) => {
                resolve(88);
            });
            console.log('computed result:', n);
        })();
    

    对了,别忘了async函数也返回的是一个Promise对象。

    END

  • 相关阅读:
    汇编指令lodsb和stosb、lodsd和stosd
    编码查询
    CLD汇编指令
    Win32编程
    MessageBox
    windows 数据类型
    STL总结
    解析结构化异常处理(SEH)(第二部分)
    FS[XX]
    ShellCode入门(提取ShellCode)
  • 原文地址:https://www.cnblogs.com/develon/p/12688160.html
Copyright © 2011-2022 走看看