Promise

作者: 世玮 | 来源:发表于2021-05-03 10:09 被阅读0次

    PromiseA+规范

    [https://promisesaplus.com/]

    术语

    1. promise 是⼀个有then⽅法的对象或者是函数,⾏为遵循本规范
    2. thenable 是⼀个有then⽅法的对象或者是函数
    3. value 是promise状态成功时的值,也就是resolve的参数, 包括各种数据类型, 也包括undefined/thenable或者是 promise
    4. reason 是promise状态失败时的值, 也就是reject的参数, 表示拒绝的原因
    5. exception 是⼀个使⽤throw抛出的异常值

    规范

    状态 promise status

    有三个状态 ,他们之间的关系
    1、pending

    • 1.1 初始状态,可以改变
    • 1.2 在resolve和reject前都处于这个状态
    • 1.3 通过resolve --》fulfilled状态
    • 1.4 通过reject --》 rejected状态
      2、fulfilled
    • 2.1 最终态, 不可变 。
    • 2.2 一个promise被resolve之后会变成这个状态
    • 2.3 必须拥有一个value的值
      3、rejected
    • 3.1 最终态,不可变。
    • 3.2 一个promise被reject之后会变成这个状态
    • 3.3 必须拥有一个reason的值

    总结一下:

    • pending ==》 resolve(value)==》 fulfilled
    • pending ==》reject(reason)==》 rejected

    then

    Promise应该提供⼀个then⽅法, ⽤来访问最终的结果, ⽆论是value还是reason.

    promise.then(onFulfilled, onRejected)
    
    1. 参数要求
    • 1.1 onFulfilled 必须是函数类型, 如果不是函数, 应该被忽略.
    • 1.2 onRejected 必须是函数类型, 如果不是函数, 应该被忽略.
    1. onFulfilled 特性
    • 2.1 在promise变成 fulfilled 时,应该调⽤ onFulfilled, 参数是value(onFulfilled的执行时机)
    • 2.2 在promise变成 fulfilled 之前, 不应该被调⽤.
    • 2.3 只能被调⽤⼀次(所以在实现的时候需要⼀个变量来限制执⾏次数)
    1. onRejected 特性
    • 3.1 在promise变成 rejected 时,应该调⽤ onRejected, 参数是reason(onRejected的执行时机)
    • 3.2 在promise变成 rejected 之前, 不应该被调⽤.
    • 3.3 只能被调⽤⼀次(所以在实现的时候需要⼀个变量来限制执⾏次数)
    1. onFulfilled 和 onRejected 应该是微任务 (宏任务 与 微任务)
      这⾥⽤queueMicrotask来实现微任务的调⽤.

    2. then⽅法可以被调⽤多次

    • 5.1 promise状态变成 fulfilled 后,所有的 onFulfilled 回调都需要按照then的顺序执⾏, 也就
      是按照注册顺序执⾏(所以在实现的时候需要⼀个数组来存放多个onFulfilled的回调)
    • 5.2 promise状态变成 rejected 后,所有的 onRejected 回调都需要按照then的顺序执⾏, 也
      就是按照注册顺序执⾏(所以在实现的时候需要⼀个数组来存放多个onRejected的回调)
    1. 返回值
      then 应该返回⼀个promise
    promise2 = promise1.then(onFulfilled, onRejected);
    
    • 6.1 onFulfilled 或 onRejected 执⾏的结果为x, 调⽤ resolvePromise
    • 6.2 如果 onFulfilled 或者 onRejected 执⾏时抛出异常e, promise2需要被reject
    • 6.3 如果 onFulfilled 不是⼀个函数, promise2 以promise1的value 触发fulfilled
    • 6.4 如果 onRejected 不是⼀个函数, promise2 以promise1的reason 触发rejected
    1. resolvePromise
    resolvePromise(promise2, x, resolve, reject)
    
    • 7.1 如果 promise2 和 x 相等,那么 reject TypeError
    • 7.2 如果 x 是⼀个 promsie
      如果x是pending态,那么promise必须要在pending,直到 x 变成 fulfilled or rejected.
      如果 x 被 fulfilled, fulfill promise with the same value.
      如果 x 被 rejected, reject promise with the same reason.
    • 7.3
      • 如果 x 是⼀个 object 或者 是⼀个 function
        let then = x.then.
      • 如果 x.then 这步出错,那么 reject promise with e as the reason.
      • 如果 then 是⼀个函数,then.call(x, resolvePromiseFn, rejectPromiseFn) resolvePromiseFn 的 ⼊参是 y, 执⾏ resolvePromise(promise2, y, resolve, reject); rejectPromise 的 ⼊参是 r, reject promise with r.
      • 如果 resolvePromise 和 rejectPromise 都调⽤了,那么第⼀个调⽤优先,后⾯的调⽤忽略。
      • 如果调⽤then抛出异常e
      • 如果 resolvePromise 或 rejectPromise 已经被调⽤,那么忽略,reject promise with e as the reason
      • 如果 then 不是⼀个function. fulfill promise with x.

    ⼀步步实现⼀个Promise;理解其巧妙的内含。

    const promise = new Promise(); //代表Promise是一个构造函数或者class
    
    //2、定义三个状态
    const PENDING = 'pending';
    const FULFILLED = 'fulfilled';
    const REJECTED = 'rejected';
    
    //1、定义一个class 
    class MyPromise {
    
        constructor(fn) {
            // 3、定义初始化状态 
            this.status = PENDING;
            this.value = null;
            this.reason = null;
    
            //5、
            try {
                //立刻执行 同步执行 。并且有异常立马reject抛出去
                fn(this.resolve.bind(this), this.reject.bind(this));
            } catch (e) {
                this.reject(e);
            }
            
            // 多个then的情况
            //let p = new Promise();
            //p.then();
            //p.then();
            this.FULFILLED_CALLBACK_LIST = [];
            this.REJECTED_CALLBACK_LIST = [];
            this._status = PENDING;
        }
    
        // FULFILLED_CALLBACK_LIST = [];
        // REJECTED_CALLBACK_LIST = [];
        // _status = PENDING;
    
        //用getter和setter代替常见的面向过程实现
        get status() {
            return this._status;
        }
    
        set status(newStatus) {
            this._status = newStatus;
            switch (newStatus) {
                case FULFILLED: {
                    this.FULFILLED_CALLBACK_LIST.forEach(callback => {
                        callback(this.value);
                    });
                    break;
                }
                case REJECTED: {
                    this.REJECTED_CALLBACK_LIST.forEach(callback => {
                        callback(this.reason);
                    });
                    break;
                }
            }
        }
    
        //4、MyPromise.resolve()  //静态方法 与 实例方法 区别 
        static resolve(value) {
            if (this.status === PENDING) {
                this.status = FULFILLED;
                this.value = value;
                // this.FULFILLED_CALLBACK_LIST.forEach(callback => {
                //     callback(this.value);
                // });
            }
        }
    
        reject(reason) {
            if (this.status === PENDING) {
                this.status = REJECTED;
                this.reason = reason;
                // this.REJECTED_CALLBACK_LIST.forEach(callback => {
                //     callback(this.reason);
                // });
            }
        }
    
        //6、
        then(onFulfilled, onRejected) {
    
            //也解决promise的 穿透
            const fulfilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => value;
            const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => reason;
    
            //这边真的巧妙,resolvePromise方便了后续的递归调用;解决了Promise的链式调用问题。
            //then操作应该是一个微任务 ;故这个函数的内部实现还需要用queueMicrotask进行包装下
            const fulFilledFnWithCatch = (resolve, reject, newPromise) => {
                try {
                    if (!this.isFunction(onFulfilled)) {
                        resolve(this.value);
                    } else {
                        const x = fulfilledFn(this.value);
                        this.resolvePromise(newPromise, x, resolve, reject);
                    }
                } catch (e) {
                    reject(e)
                }
            };
    
            const rejectedFnWithCatch = (resolve, reject, newPromise) => {
                try {
                    if (!this.isFunction(onRejected)) {
                        reject(this.reason);
                    } else {
                        const x = rejectedFn(this.reason);
                        this.resolvePromise(newPromise, x, resolve, reject);
                    }
                } catch (e) {
                    reject(e);
                }
            }
    
            switch (this.status) {
                case FULFILLED: {
                    const newPromise = new MyPromise((resolve, reject) =>
                        fulFilledFnWithCatch(resolve, reject, newPromise));
                    return newPromise;
                }
                case REJECTED: {
                    const newPromise = new MyPromise((resolve, reject) =>
                        rejectedFnWithCatch(resolve, reject, newPromise));
                    return newPromise;
                }
                case PENDING: {
                    const newPromise = new MyPromise((resolve, reject) => {
                        this.FULFILLED_CALLBACK_LIST.push(() =>
                            fulFilledFnWithCatch(resolve, reject, newPromise));
                        this.REJECTED_CALLBACK_LIST.push(() =>
                            rejectedFnWithCatch(resolve, reject, newPromise));
                    });
                    return newPromise;
                }
                default: {
                    break;
                }
            }
        }
    
        catch (onRejected) {
            return this.then(null, onRejected);
        }
    
        resolvePromise(newPromise, x, resolve, reject) {
            // 如果 newPromise 和 x 指向同⼀对象,以 TypeError 为据因拒绝执⾏ newPromise
            // 这是为了防⽌死循环
            if (newPromise === x) {
                return reject(new TypeError('The promise and the return value are the same'));
            }
            if (x instanceof MyPromise) {
                // 如果 x 为 Promise ,则使 newPromise 接受 x 的状态
                // 也就是继续执⾏x,如果执⾏的时候拿到⼀个y,还要继续解析y
                // 这个if跟下⾯判断then然后拿到执⾏其实重复了,可有可⽆
                x.then((y) => {
                    resolvePromise(newPromise, y, resolve, reject);
                }, reject);
            } else if (typeof x === 'object' || this.isFunction(x)) {
                // 如果 x 为对象或者函数
                // 这个坑是跑测试的时候发现的,如果x是null,应该直接resolve
                if (x === null) {
                    return resolve(x);
                }
                let then = null;
                try {
                    // 把 x.then 赋值给 then 
                    then = x.then;
                } catch (error) {
                    // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
                    return reject(error);
                }
                // 如果 then 是函数
                if (this.isFunction(then)) {
                
                    //防止多次调用,是一个防并发的操作
                    let called = false;
                    // 将 x 作为函数的作⽤域 this 调⽤之
                    // 传递两个回调函数作为参数,第⼀个参数叫做 resolvePromise ,第⼆个参数叫做 rejectPromise
                    // 名字重名了,我直接⽤匿名函数了
                    try {
                        then.call(x,
                            // 如果 resolvePromise 以值 y 为参数被调⽤,则运⾏resolvePromise
                            (y) => {
                                // 如果 resolvePromise 和 rejectPromise 均被调⽤,
                                // 或者被同⼀参数调⽤了多次,则优先采⽤⾸次调⽤并忽略剩下的调⽤
                                // 实现这条需要前⾯加⼀个变量called
                                if (called) return;
                                called = true;
                                resolvePromise(promise, y, resolve, reject);
                            },
                            // 如果 rejectPromise 以据因 r 为参数被调⽤,则以据因 r 拒绝promise
                            (r) => {
                                if (called) return;
                                called = true;
                                reject(r);
                            });
                    } catch (error) {
                        // 如果调⽤ then ⽅法抛出了异常 e:
                        // 如果 resolvePromise 或 rejectPromise 已经被调⽤,则忽略之
                        if (called) return;
                        // 否则以 e 为据因拒绝 promise
                        reject(error);
                    }
                } else {
                    // 如果 then 不是函数,以 x 为参数执⾏ promise
                    resolve(x);
                }
            } else {
                // 如果 x 不为对象或者函数,以 x 为参数执⾏ promise
                resolve(x);
            }
        }
    
        isFunction(params) {
            return typeof params === 'function';
        }
        
            static resolve(value) {
            if (value instanceof MyPromise) {
                return value;
            }
    
            return new MyPromise((resolve) => {
                resolve(value);
            });
        }
    
        static reject(reason) {
            return new MyPromise((resolve, reject) => {
                reject(reason);
            });
        }
    
        static race(promiseList) {
            return new MyPromise((resolve, reject) => {
                const length = promiseList.length;
    
                if (length === 0) {
                    return resolve();
                } else {
                    for (let i = 0; i < length; i++) {
                        MyPromise.resolve(promiseList[i]).then(
                            (value) => {
                                return resolve(value);
                            },
                            (reason) => {
                                return reject(reason);
                            });
                    }
                }
            });
    
        }
    }
    

    自动化测试

    npm i promises-aplus-tests -g
    
    MyPromise.defer = MyPromise.deferred = function () {
      let dfd = {}
      dfd.promise = new MyPromise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
      });
      return dfd;
    }
    

    Promise常用示例

    • 异步加载图片
    function loadImageAsync(url) {
      return new Promise(function(resolve, reject) {
        const image = new Image();
    
        image.onload = function() {
          resolve(image);
        };
    
        image.onerror = function() {
          reject(new Error('Could not load image at ' + url));
        };
    
        image.src = url;
      });
    }
    
    • Ajax
    const getJSON = function(url) {
      const promise = new Promise(function(resolve, reject){
        const handler = function() {
          if (this.readyState !== 4) {
            return;
          }
          if (this.status === 200) {
            resolve(this.response);
          } else {
            reject(new Error(this.statusText));
          }
        };
        const client = new XMLHttpRequest();
        client.open("GET", url);
        client.onreadystatechange = handler;
        client.responseType = "json";
        client.setRequestHeader("Accept", "application/json");
        client.send();
    
      });
    
      return promise;
    };
    
    • 立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务; 但是加上return语句,就不同了。
    new Promise((resolve, reject) => {
      resolve(1);
      console.log(2);
    }).then(r => {
      console.log(r);
    });
    // 2
    // 1
    new Promise((resolve, reject) => {
      return resolve(1);
      // 后面的语句不会执行
      console.log(2);
    }).then(r => {
      console.log(r);
    });
    // 1
    
    • Promise.all场景:
    const p1 = new Promise((resolve, reject) => {
      resolve('hello');
    })
    .then(result => result)
    .catch(e => e);
    
    const p2 = new Promise((resolve, reject) => {
      throw new Error('报错了');
    })
    .then(result => result)
    .catch(e => e);
    
    Promise.all([p1, p2])
    .then(result => console.log(result))
    .catch(e => console.log(e));
    // ["hello", Error: 报错了]
    
      • p1会resolved,p2首先会rejected,但是p2有自己的catch方法,该方法返回的是一个新的 Promise 实例,p2指向的实际上是这个实例。该实例执行完catch方法后,也会变成resolved,导致Promise.all()方法参数里面的两个实例都会resolved,因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数
      • 如果p2没有自己的catch方法,就会调用Promise.all()的catch方法。
    • Promise.allSettled()
    const resolved = Promise.resolve(42);
    const rejected = Promise.reject(-1);
    
    const allSettledPromise = Promise.allSettled([resolved, rejected]);
    
    allSettledPromise.then(function (results) {
      console.log(results);
    });
    // [
    //    { status: 'fulfilled', value: 42 },
    //    { status: 'rejected', reason: -1 }
    // ]
    
      • 该方法返回的新的 Promise 实例,一旦结束,状态总是fulfilled,不会变成rejected。状态变成fulfilled后,Promise 的监听函数接收到的参数是一个数组,每个成员对应一个传入Promise.allSettled()的 Promise 实例
      • 每个对象都有status属性,该属性的值只可能是字符串fulfilled或字符串rejected。fulfilled时,对象有value属性,rejected时有reason属性,对应两种状态的返回值

    迭代器 Iterator

    迭代器Iterator 是 ES6 引入的一种新的遍历机制,同时也是一种特殊对象,它具有一些专门为迭代过程设计的专有接口。

    每个迭代器对象都有一个next()方法,每次调用都返回一个当前结果对象。当前结果对象中有两个属性:

    1. value:当前属性的值

    2. done:用于判断是否遍历结束,当没有更多可返回的数据时,返回true

    每调用一次next()方法,都会返回下一个可用的值,直到遍历结束。

    生成器 Generator

    • 生成器是一种返回迭代器的函数,通过function关键字后的星号(*)来表示,函数中会用到新的关键字yield。星号可以紧挨着function关键字,也可以在中间添加一个空格.
    • 形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态

    特性

    1. 每当执行完一条yield语句后函数就会自动停止执行, 直到再次调用next();
    2. yield关键字只可在生成器内部使用,在其他地方使用会导致程序抛出错误;
    3. 可以通过函数表达式来创建生成器, 但是不能使用箭头函数
      let generator = function *(){}
    function getFoo () {
      return new Promise(function (resolve, reject){
        resolve('foo');
      });
    }
    
    const g = function* () {
      try {
        const foo = yield getFoo();
        console.log(foo);
      } catch (e) {
        console.log(e);
      }
    };
    
    function run (generator) {
      const it = generator();
    
      function go(result) {
        if (result.done) return result.value;
    
        return result.value.then(function (value) {
          return go(it.next(value));
        }, function (error) {
          return go(it.throw(error));
        });
      }
    
      go(it.next());
    }
    
    run(g);
    
    • 除了for...of循环以外,扩展运算符(...)、解构赋值和Array.from方法内部调用的,都是遍历器接口。这意味着,它们都可以将 Generator 函数返回的 Iterator 对象,作为参数
    function* numbers () {
      yield 1
      yield 2
      return 3
      yield 4
    }
    
    // 扩展运算符
    [...numbers()] // [1, 2]
    
    // Array.from 方法
    Array.from(numbers()) // [1, 2]
    
    // 解构赋值
    let [x, y] = numbers();
    x // 1
    y // 2
    
    // for...of 循环
    for (let n of numbers()) {
      console.log(n)
    }
    // 1
    // 2
    
    • 如果 Generator 函数内部有try...finally代码块,且正在执行try代码块,那么return()方法会导致立刻进入finally代码块,执行完以后,整个函数才会结束
    function* numbers () {
      yield 1;
      try {
        yield 2;
        yield 3;
      } finally {
        yield 4;
        yield 5;
      }
      yield 6;
    }
    var g = numbers();
    g.next() // { value: 1, done: false }
    g.next() // { value: 2, done: false }
    g.return(7) // { value: 4, done: false }
    g.next() // { value: 5, done: false }
    g.next() // { value: 7, done: true }
    
    • 自执行函数;自执行函数的作用是将generator函数转换为同步化顺序执行的函数
      • Thunkify实现自执行函数
    import fetch from 'isomorphic-fetch';
    //thunkify是一个nodejs库
    const thunkify = require('thunkify');
    const fetchThunk = thunkify(fetch);
    
    const genFnAsync = async function (){
        console.log('step 1');
        var f1 = await fetch('http://localhost:9000/h5/api/mobile/unionFrontVersion');
        console.log('step 2', f1.json().then((data)=>{console.log(data)}));
        var f2 = await fetch('http://localhost:9000/h5/api/mobile/unionFrontVersion');
        console.log('step 3', f2);
        var f3 = await "213213";
        console.log('step 4', f3);
    };
    
    
    const thenableResolve = {
        then: function (resolve, reject) {
            resolve("thenableResolve");
        }
    }
    const thenableReject = {
        then: function (resolve, reject) {
            reject("thenableReject");
        }
    }
    
    //ES5
    var Thunk5 = function (fn) {
        return function () {
            var args = Array.prototype.slice.call(arguments);
            return function (callback) {
                args.push(callback);
                return fn.apply(this, args);
            }
        }
    }
    
    //ES6
    const Thunk6 = function (fn) {
        return function (...args) {
            return function (callback) {
                return fn.call(this, ...args, callback);
            }
        }
    }
    
    const isPromise = (obj)=>{
        return 'function' == typeof obj.then;
    }
    
    const run = function (fn) {
        var gen = fn();
        function next(error, data) {
            var result = gen.next(data);
            if(result.done) return;
            //启动函数为 promise对象
            if (isPromise(result.value)) {
                result.value.then(function(data) {
                    next(data);
                });
            } else {
                //启动函数为 回调函数
                result.value(next)
            }
        }
        next();
    }
    
      • 使用co自动化执行Generator函数;co 函数返回一个 Promise 对象,因此可以用 then 方法添加回调函数。
    var co = require('co');
    var gen = function* (){
      var f1 = yield readFile('/core/test1');
      var f2 = yield readFile('/core/test2');
      console.log(f1.toString());
      console.log(f2.toString());
    };
    co(gen).then(function (){
      console.log('Generator 函数执行完成');
    })
    
    • (1)回调函数。将异步操作包装成 Thunk 函数,在回调函数里面交回执行权。

    • (2)Promise 对象。将异步操作包装成 Promise 对象,用 then 方法交回执行权。

    co 函数库其实就是将两种自动执行器(Thunk 函数和 Promise 对象),包装成一个库。使用 co 的前提条件是,Generator 函数的 yield 命令后面,只能是 Thunk 函数或 Promise 对象。

    • co 支持并发的异步操作,即允许某些操作同时进行,等到它们全部完成,才进行下一步。这时,要把并发的操作都放在数组或对象里面。
    // 数组的写法
    co(function* () {
      var res = yield [
        Promise.resolve(1),
        Promise.resolve(2)
      ];
      console.log(res); 
    }).catch(onerror);
    
    // 对象的写法
    co(function* () {
      var res = yield {
        1: Promise.resolve(1),
        2: Promise.resolve(2),
      };
      console.log(res); 
    }).catch(onerror);
    

    相关文章

      网友评论

          本文标题:Promise

          本文链接:https://www.haomeiwen.com/subject/wbitdltx.html