Promise

2021-05-03  本文已影响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

总结一下:

then

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

promise.then(onFulfilled, onRejected)
  1. 参数要求
  1. onFulfilled 特性
  1. onRejected 特性
  1. onFulfilled 和 onRejected 应该是微任务 (宏任务 与 微任务)
    这⾥⽤queueMicrotask来实现微任务的调⽤.

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

  1. 返回值
    then 应该返回⼀个promise
promise2 = promise1.then(onFulfilled, onRejected);
  1. resolvePromise
resolvePromise(promise2, x, resolve, reject)

⼀步步实现⼀个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;
  });
}
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;
};
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
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: 报错了]
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 }
// ]

迭代器 Iterator

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

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

  1. value:当前属性的值

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

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

生成器 Generator

特性

  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);
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
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 }
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();
}
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 函数执行完成');
})

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

// 数组的写法
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);
上一篇下一篇

猜你喜欢

热点阅读