实现符合PromiseA+规范的Promise

336 阅读12分钟

一、准备工作


在学习promise前,我们先了解几个基础概念。

1. 高阶函数扩展方法

// 核心代码
function core(...args) {
    console.log('core', args);
}

// 给core增加额外逻辑,但是不改变原有核心代码
Function.prototype.before = function(cb) {
    // this => core
    return (...args) => {
        cb();
        this(...args);
    }
}

let newCore = core.before(() => {
    console.log('before');
})

newCore('a', 'b');
// before
// core ['a', 'b']

函数定义的作用域和执行的作用域不在同一个,肯定会出现闭包

function a() {
    function b() {

    }
}
b() // b函数在a函数作用域外执行,a不会被销毁

2. 函数柯里化

// 记录传入的参数,并且和函数的参数个数做比较,参数个数一致,执行函数
function curring(fn) {
    const inner = (args = []) => {
        return args.length >= fn.length ? fn(...args) : (...userArgs) => inner([...args, ...userArgs]);
    }
    return inner();
}

function sum(a, b, c, d) {
    return a + b + c + d;
}

let sum1 = curring(sum);
let sum2 = sum1(1);
let sum3 = sum2(2, 3);
let result = sum3(4);

console.log(result); // 10

柯里化的应用:数据类型判断

function curring(fn) {
    const inner = (args = []) => {
        return args.length >= fn.length ? fn(...args) : (...userArgs) => inner([...args, ...userArgs]);
    }
    return inner();
}

function isType(typing, val) {
    return Object.prototype.toString.call(val) === `[object ${typing}]`;
}
let util = {};
['String', 'Number', 'Boolean', 'Null', 'Undefined'].forEach(type => {
    util[`is${type}`] = curring(isType)(type);
})
console.log(util.isString('abc'));

3. compose函数

function addPrefix(str) {
    return '$' + str;
}
function len(str) {
    return str.length;
}
function sum(a, b) {
    return a + b
}

const compose = (...fns) => {
    return fns.reduce(function(a, b) {
        return function (...args) {
            return a(b(...args));
        }
    })
}

let final = compose(addPrefix, len, sum);
const r = final('a', 'b');
console.log(r); // $2

4. 发布订阅模式,解决异步并发问题

class Event {
    constructor() {
        this._events = [];
    }
    on(fn) {
        this._events.push(fn);
    }
    emit(data) {
        this._events.forEach(fn => fn(data));
    }
}
function EventEmitter() {
    this._events = {};
}
EventEmitter.prototype.on = function (eventName, callback) {
    // 实例上可能没有events,没有我们就给他加一个
    if (!this._events) {
        this._events = {};
    }
    if (this._events[eventName]) {
        this._events[eventName].push(callback);
    } else {
        this._events[eventName] = [callback];
    }
}
EventEmitter.prototype.once = function (eventName, callback) {
    // AOP切片,触发之后就移除
    const one = () => {
        callback();
        this.off(eventName, callback);
    }
    one.l = callback; // 为了避免用户使用时,刚once了,立刻又off的情况(这种情况下不执行)
    this.on(eventName, one);
}
EventEmitter.prototype.emit = function (eventName, ...args) {
    this._events[eventName].forEach(fn => {
        fn(...args);
    })
}
EventEmitter.prototype.off = function (eventName, callback) {
    if (this._events && this._events[eventName]) {
        // off的时候,将on绑定的和once绑定的对应事件名都移除
        this._events[eventName] = this._events[eventName].filter(fn => fn !== callback && fn.l !== callback);
    }
}

module.exports = EventEmitter;

function Boy() { }
// 原型链继承
Boy.prototype.__proto__ = EventEmitter.prototype;
// Object.setPrototypeOf(Boy.prototype, EventEmitter.prototype);
// Boy.prototype = create(EventEmitter.prototype);
// function create(proto) {
//     function Fn() { }
//     Fn.prototype = proto;
//     return new Fn();
// }

let boy = new Boy();
boy.on('xxx', (y) => {
    console.log(111, y);
})
boy.on('xxx', (y) => {
    console.log(222, y);
})
boy.once('yyy', () => {
    console.log(333);
})
setTimeout(() => {
    boy.emit('xxx', 'yyy')
    boy.emit('yyy');
    boy.emit('yyy')
}, 1000)

5. 观察者模式

// 被观察者
class Subject {
    constructor(name) {
        this.name = name;
        this.state = 'happy';
        this.observers = [];
    }
    // 被观察者要收集观察者,后续状态改变,通知观察者
    attach(o) {
        this.observers.push(o);
    }
    setState(newState) {
        this.state = newState;
        this.observers.forEach(o => o.update(this.name, newState));
    }
    
}
// 观察者
class Observer {
    constructor(name) {
        this.name = name;
    }
    update(baby, state) {
        console.log(this.name + ',' + baby + '现在' + state);
    }
}

let baby = new Subject('xiaoming');
let dad = new Observer('dad');
let mam = new Observer('mom');

baby.attach(dad);
baby.attach(mam);
baby.setState('crying');

二、Promise


Promise解决了什么问题:

  1. 链式调用解决嵌套回调的问题。
  2. 同步并发问题
  3. 多个异步处理问题

1. 基础promise

// 1. promise是一个类,使用时new
// 2. 使用promise时,传入executor执行器,次函数是立即执行的。
// 3. executor函数接收两个参数,resolve和reject,分别更新当前promise为成功或失败状态
// 4. promise有三种状态,pending, fulfilled, rejected状态变化后不能更改,不可逆。
// 5. 抛出错误,状态变为rejected。
// 6. 每个promise实例都有一个then方法,then是微任务,异步的。

const Promise = require('./src/1.promise.js');

let promise = new Promise((resolve, reject) => {
    resolve('成功');
    // new Error('xxx');
})

promise.then((val) => {
    console.log('success', val);
}, (reason) => {
    console.log('err', reason);
})
// 1.promise.js
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;  // 成功的数据
        this.reason = undefined; // 失败的数据

        const resolve = (value) => {
            if (this.status === PENDING) {
                this.status = FULFILLED;
                this.value = value;
            }
        }

        const reject = (reason) => {
            if (this.status === PENDING) {
                this.status = REJECTED;
                this.reason = reason;
            }
        }
        try {
            executor(resolve, reject);
        } catch(e) {
            reject(e);
        }
    }
    then(onFulfilled, onRejected) {
        if (this.status === FULFILLED) {
            onFulfilled(this.value);
        }
        if (this.status === REJECTED) {
            onRejected(this.reason);
        }
    }
}

module.exports = Promise;

2. 异步promise

如果在我们延迟几秒resolve,怎么处理呢?
当调用then方法时,promise的状态可能是pending,此时需要把成功和失败回调先存起来,等待状态变更后,再执行onFullFilledonRejected(发布订阅)。

const Promise = require('./src/2.promise.js');
let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功');
    }, 2000)
})

promise.then((val) => {
    console.log('success', val);
}, (reason) => {
    console.log('err', reason);
})
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;  // 成功的数据
        this.reason = undefined; // 失败的数据
+       this.onResolvedCallbacks = [];
+       this.onRejectedCallbacks = [];

        const resolve = (value) => {
            if (this.status === PENDING) {
                this.status = FULFILLED;
                this.value = value;
                // 发布
+               this.onResolvedCallbacks.forEach(fn => fn());
            }
        }

        const reject = (reason) => {
            if (this.status === PENDING) {
                this.status = REJECTED;
                this.reason = reason;
                // 发布
+               this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject);
        } catch(e) {
            reject(e);
        }
    }
    then(onFulfilled, onRejected) {
        // 同步执行
        if (this.status === FULFILLED) {
            onFulfilled(this.value);
        }
        if (this.status === REJECTED) {
            onRejected(this.reason);
        }
        // 有定时器的情形,先保存起来,resolve的时候依次调用(订阅)
+       if (this.status === PENDING) {
+           this.onResolvedCallbacks.push(() => { //AOP
+               // todo...
+               onFulfilled(this.value);
+           });
+           this.onRejectedCallbacks.push(() => {
+               onRejected(this.reason);
+           });
+       }
    }
}

module.exports = Promise;

3. 链式调用

Promise链式调用,当调用then方法后,会返回一个新的Promise

// 利用x的值来判断是调用promise2的resolve还是reject
function resolvePromise(promise2, x, resolve, reject) {
    // 1. x的值是promise2
    if (promise2 === x) {
        return reject(new TypeError('类型错误'));
    }
    // 2. 兼容别人的promise,核心就是判断如果是promise就调then,不是promise就直接返回
    // 1) 对象或者函数,有可能是promise
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        try { // 有可能then方法是通过defineProperty来实现的,取值是可能会发生异常
            let then = x.then;
            if (typeof then === 'function') { // 认为是promise了
                then.call(x, y => {  // 为什么不直接x.then这么写呢?因为这样写会触发getter,可能会发生异常
                    resolve(y);
                }, r => {
                    reject(r);
                });
            } else {
                resolve(x);
            }
        } catch(e) {
            reject(e);
        }
    // 2)肯定不是promise,是普通值。直接放到promise2.resolve中
    } else {
        resolve(x);
    }
}
// then方法
then(onFulfilled, onRejected) {
    // then调用后,返回一个新promise
    let promise2 = new Promise((resolve, reject) => {
        // 直接成功或失败
        if (this.status === FULFILLED) {
            setTimeout(() => { // 加定时器,异步的目的是拿到promise2
                try {
                    let x = onFulfilled(this.value);
                    // x有可能是promise,如果是promise需要看下这个promise是成功还是失败,成功则把成功的结果调用promise2的resolve传递进去。
                    // 总结:x的值决定是调用promise2的resolve还是reject,如果是promise则取他的状态,如果是普通值,则直接调用resolve;
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            }, 0);
        }
        if (this.status === REJECTED) {
            setTimeout(() => {
                try {
                    let x = onRejected(this.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            }, 0);
        }
        // 有定时器的情形,先保存起来,resolve的时候依次调用(订阅)
        if (this.status === PENDING) {
            this.onResolvedCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0);
            });
            this.onRejectedCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0);
            });
        }
    })
    return promise2;
}
1)情形一:then中方法返回的是一个普通值(不是promise)的情况,会作为外层下一次then的成功结果。
let promise = new Promise((resolve) => {
    resolve('ok');
}).then((data) => {
    return 1;
}, err => {
    console.log('err', err);
}).then((data) => {
    console.log('成功', data); // '成功' 1
}, err => {
    console.log('err', err);
})
2)情形二:then中方法执行出错,会走到外层下一次then的失败结果
let promise = new Promise((resolve) => {
    resolve('ok');
}).then((data) => {
    throw new Error('出错'); // 抛错
}, err => {
    console.log('err', err);
}).then((data) => {
    console.log('成功', data); 
}, err => {
    console.log('err', err);// 'err' '出错'
})
3)情形三:如果then中方法返回的是一个promise对象,此时会根据promise的结果来处理是走成功还是失败
let promise2 = new Promise((resolve) => {
    resolve(1);
}).then((data) => {
    return new Promise((resolve, reject) => { // x 可能是promise
        setTimeout(() => {
            resolve('ok');
            // reject('错误');
        }, 1000)
    })
})
promise2.then((data) => {
    console.log('成功', data); // 成功  'ok'
}, err => {
    console.log('err', err);
})

4. 嵌套promise的处理

// 嵌套promise
let promise2 = new Promise((resolve) => {
    resolve(1);
}).then((data) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(new Promise((resolve, reject) => { // resolve的还是一个promise
                setTimeout(() => {
                    resolve('ok');
                }, 1000)
            }));
        }, 1000)
    })
})
promise2.then((data) => {
    console.log('成功', data); // 成功 ok
}, err => {
    console.log('err', err);
})

怎么处理呢?

// 利用x的值来判断是调用promise2的resolve还是reject
function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
        return reject(new TypeError('类型错误'));
    }
    // 1) 对象或者函数,有可能是promise
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        try {
            let then = x.then;
            if (typeof then === 'function') {
                then.call(x, y => {
+                   resolvePromise(promise2, y, resolve, reject); // 嵌套promise的情况:resolve的值还是promise
                }, r => {
                    reject(r);
                });
            } else {
                resolve(x);
            }
        } catch(e) {
            reject(e);
        }
    // 2)肯定不是promise,是普通值。直接放到promise2.resolve中
    } else {
        resolve(x);
    }
}

5. 值得穿透,怎么实现

// 值得穿透
new Promise((resolve) => {
    resolve('ok');
}).then().then().then((data) => {
    console.log('成功', data); // 成功 ok
}, err => {
    console.log('err', err);
})

new Promise((resolve, reject) => {
    reject('err');
}).then().then().then((data) => {
    console.log('成功', data);
}, err => {
    console.log('err', err); // err err
})

then的参数是可选的,不传就给他一个默认的函数。

then(onFulfilled, onRejected) {
    // 穿透问题
+   onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
+   onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
    // then调用后,返回一个新promise
    let promise2 = new Promise((resolve, reject) => {
        ...
    })
    return promise2;
}

到这里,满足PromiseA+规范的promise就完成了,我们再看看规范外应用比较广泛的一些内容。

6. 延迟对象

// 延迟对象,帮我们减少一次套用
Promise.deferred = function() {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd;
}
// 正常写法
function readFile(filePath, encoding) {
    return new Promise((resolve, reject) => {
        fs.readFile(filePath, encoding, (err, data) => {
            if (err) return inject(err);
            resolve(data);
        })
    })
}
// 使用延迟套用的写法
function readFile(filePath, encoding) {
    let dfd = Promise.deferred;
    fs.readFile(filePath, encoding, (err, data) => {
        if (err) return dfd.inject(err);
        dfd.resolve(data);
    })
    return dfd.promise;
}

7. promise直接resolve一个promise

new Promise((resolve, reject) => {
    resolve(new Promise((resolve, reject) => {
        resolve(100)
    }))
}).then(data => {
    console.log('成功', data);  // 成功 100
})

resolve时,判断如果是promise的实例,则直接调用promise的then方法。

class Promise {
    constructor(executor) {
        ...
        const resolve = (value) => {
            // 直接resolve一个promise
+           if (value instanceof Promise) {
+               return value.then(resolve, reject);
+           }
        }

        const reject = (reason) => {
            ...
        }
        try {
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }
    then(onFulfilled, onRejected) {
        ...
    }
}

8. promise几个静态方法

1)Promise.resolve、Promise.reject、catch
// Promise.resolve() 方法
Promise.resolve('ok').then((data) => {
    console.log('成功', data); // 成功 ok 
})

// Promise.reject() 方法
Promise.reject(new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('ok');
    }, 1000);
})).then((data) => {
    console.log('成功', data);
}, err => {
    console.log('err', err); // err
})

// catch
Promise.reject(new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('ok');
    }, 1000);
})).then((data) => {
    console.log('成功', data);
}).catch(err => {
    console.log('err', err); // err
})

实现原理如下:

class Promise {
    then(onFulfilled, onRejected) {
        ...
    }
    catch(errorFn) {
        return this.then(null, errorFn);
    }
    static resolve(value) {
        return new Promise((resolve, reject) => {
            resolve(value);
        })
    }
    static reject(value) {
        return new Promise((resolve, reject) => {
            reject(value);
        })
    }
}
2) Promise.all方法

同一时刻拿到多个异步请求的结果。
Promise.all有个缺点:只要其中一个失败了,整个Promise就失败了。

Promise.all([1, 2, 3, new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('ok');
    }, 2000)
})]).then((data) => {
    console.log('成功', data); // 成功 [1, 2, 3, 'ok']
}).catch(e => {
    console.log('失败', e);
})

实现原理如下:

// Promise.all
Promise.all = function(promises) {
    return new Promise((resolve, reject) => {
        let result = [];
        let times = 0;

        const processSuccess = (index, val) => {
            result[index] = val;
            if (++times === promises.length) {
                resolve(result);
            }
        }

        for (let i = 0; i < promises.length; i++) {
            let p = promises[i];
            if (p && typeof p.then === 'function') {
                p.then((data) => {
                    processSuccess(i, data);
                }, reject);
            } else {
                processSuccess(i, p);
            }
        }
    })
}
3) Promise.race
let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    }, 1000)
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('失败')
    }, 2000)
})

Promise.race = function(promises) {
    return new Promise((resolve, reject) => {
        for (let i = 0; i< promises.length; i++) {
            let p = promises[i];
            if (p && typeof p.then === 'function') {
                p.then(resolve, reject)
            } else {
                resolve(p)
            }
        }
    })
}
Promise.race([p1, p2]).then((data) => {
    console.log(data);
}, (err) => {
    console.log(err);
})

Promise.race的使用场景:图片加载、请求加载超时、脚本加载超时处理(超时后,即使结果返回也不采用成功的结果了)。

let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    }, 2000)
})
function wrap(p1) {
    let abort;
    let p = new Promise((resolve, reject) => {
        abort = reject;
    });
    let p2 = Promise.race([p, p1]);
    p2.abort = abort;
    return p2;
}
let p2 = wrap(p1);
p2.then((data) => {
    console.log(data);
}, (err) => {
    console.log(err);
});

setTimeout(() => {
    p2.abort('超过1s了');
}, 1000);
4) Promise.prototype.finally

Promise.prototype.finally原型方法,无论状态如何都会执行,而且可以继续向下执行。

let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
        // reject('失败')
    }, 3000);
}).finally((data) => {
    console.log('finally', data); // finally undefined => finally接受不到参数,但是可以把resolve的返回值透传下去
}).then((data) => {
    console.log('resolve', data); // resolve  成功
}).catch((data) => {
    console.log('reject', data);  // reject  失败
})
let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
        // reject('失败')
    }, 3000);
}).finally((data) => {
    console.log('finally', data); // finally undefined 
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('xxx');
            // reject('yyy')
        }, 2000);
    })
}).then((data) => {
    console.log('resolve', data); // resolve  成功
}).catch((data) => {
    console.log('reject', data);  // reject  yyy
})

finally的特点:

  • 无论状态为成功还是失败,都可以继续往下执行。
  • finally没有参数,会把excutor中的返回值,透传下去。如果是resolve,则透传给thenreject则透传给catch
  • finally返回一个promise时,如果promiseresolve了值,则忽略。传递给then的仍旧是excutorresolve值。如果promisereject了值,则将reject的值传递给catch

实现一个Promise.prototype.finally

Promise.prototype.finally = function(cb) {
    // then是为了等待里层promise执行完
    return this.then((data) => {
        // Promise.resolve实现等待效果,等待cb执行完,然后将excutor中的值传递下去
        return Promise.resolve(cb()).then(() => data); // Promise.resolve保证promise执行完毕
    }, (err) => {
        return Promise.resolve(cb()).then(() => { throw err });
    })
}
5) Promise.allSettled

不管是成功还是失败,会得到所有结果,不会走catch方法。

// Promise.allSettled
Promise.allSettled = function(promises) {
    return new Promise((resolve, reject) => {
        let result = [];
        let times = 0;

        const processSuccess = (index, val) => {
            result[index] = val;
            if (++times === promises.length) {
                resolve(result);
            }
        }

        for (let i = 0; i < promises.length; i++) {
            let p = promises[i];
            if (p && typeof p.then === 'function') {
                p.then((data) => {
                    processSuccess(i, { status: 'fulfilled', value: data });
                }).catch((err) => {
                    processSuccess(i, { status: 'rejected', reason: err });
                })
            } else {
                processSuccess(i, p);
            }
        }
    })
}
Promise.allSettled([1, 2, 3, new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('错误');
    }, 2000)
})]).then((data) => {
    console.log('成功', data); // 成功 [ 1, 2, 3, { status: 'rejected', reason: '错误' } ]
}).catch(e => {
    console.log('失败', e);
})
6) Promise.any

如果其中一个成功了,就走成功,返回的是第一个成功的值。都失败了才会走失败。

let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    }, 1000)
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('失败')
    }, 2000)
})

Promise.any = function(promises) {
    return new Promise((resolve, reject) => {
        let result = [];
        let times = 0;

        const processSuccess = (index, val) => {
            result[index] = val;
            if (++times === promises.length) {
                reject(result);
            }
        }

        for (let i = 0; i < promises.length; i++) {
            let p = promises[i];
            if (p && typeof p.then === 'function') {
                p.then((data) => {
                    resolve(data);
                }).catch(err => {
                    processSuccess(i, err)
                });
            } else {
                resolve(p);
            }
        }
    })
}
Promise.any([p1, p2]).then((data) => {
    console.log(data); // 成功
}, (err) => {
    console.log(err);
})
7) promisify

promisify,主要功能是将一个异步的方法转化成promise的形式。主要给node来使用的。
回调函数的参数永远第一个是error

function promisify(readFile) {
    return function (...args) {
        return new Promise((resolve, reject) => {
            readFile(...args, (err, data) => {
                if (err) return reject(err);
                resolve(data);
            })
        })
    }
}

const fs = require('fs');
let readFile = promisify(fs.readFile);

readFile('./.gitignore', 'utf8').then((data) => {
    console.log(data);
})
8) promisifyAll

把对象里的方法都转成promise的形式。

function promisifyAll(obj) {
    let o = {};
    for (let key in obj) {
        if (typeof obj[key] === 'function') {
            o[key+'Promise'] = promisify(obj[key])
        }
    }
    return o;
}
const fs = require('fs');
let newFs = promisifyAll(fs);
newFs.readFilePromise('./.gitignore', 'utf8').then((data) => {
    console.log(data);
})

三、generator函数


generator函数核心是通过switch case实现的。

function* read() {
    console.log('first');
    yield 1;
    console.log('second');
    yield 2;
    console.log('third');
    yield 3;
}

let it = read(); // 迭代器
it.next(); // first
it.next(); // second
it.next(); // third

实现原理:

let regeneratorRuntime = {
    wrap(iteratorFn) {
        const context = {
            prev: 0,
            next: 0,
            done: false,
            stop() {
                this.done = true;
            }
        }
        // 迭代器
        let it = {};
        it.next = function() {
            let value = iteratorFn(context);
            return {
                value,
                done: context.done
            }
        }
        return it;
    }
}

function read() {
    return regeneratorRuntime.wrap(function read$(_context) {
        while (1) {
            switch (_context.prev = _context.next) {
                case 0:
                    console.log('first');
                    _context.next = 3;
                    return 1;

                case 3:
                    console.log('second');
                    _context.next = 6;
                    return 2;

                case 6:
                    console.log('third');
                    _context.next = 9;
                    return 3;

                case 9:
                    case "end":
                    return _context.stop();
            }
        }
    });
}

let it = read(); // 迭代器
{
    let { value, done } = it.next(); // first
    console.log(value, done); // 1 false
}
{
    let { value, done } = it.next(); // second
    console.log(value, done); // 2 false
}
{
    let { value, done } = it.next(); // third
    console.log(value, done); // 3 false
}
{
    let { value, done } = it.next();
    console.log(value, done); // undefined done
}

generator next传参的情形:

function* read() {
    var a = yield 1;
    console.log(a);
    var b = yield 2;
    console.log(b);
    var c = yield 3;
    console.log(c);
}

let it = read(); // 迭代器
{
    let {value, done} = it.next('xxx'); // 第一个传参也没用,没做处理,设计如此
    console.log(value, done); // 1 false
}
{
    let {value, done} = it.next('aaa'); // aaa
    console.log(value, done); // 2 false
}
{
    let {value, done} = it.next('bbb'); // bbb
    console.log(value, done); // 3 false
}
{
    let {value, done} = it.next('ccc'); // ccc
    console.log(value, done); // undefined done
}

执行流程:

image.png

image.png

实现原理:

let regeneratorRuntime = {
    wrap(iteratorFn) {
        const context = {
            prev: 0,
            next: 0,
            sent: '',
            done: false,
            stop() {
                this.done = true;
            }
        }
        // 迭代器
        let it = {};
        it.next = function (v) {
            context.sent = v; // next传参
            let value = iteratorFn(context);
            return {
                value,
                done: context.done
            }
        }
        return it;
    }
}

function read() {
    return regeneratorRuntime.wrap(function read$(_context) {
        while (1) {
            switch (_context.prev = _context.next) {
                case 0:
                    _context.next = 2;
                    return 1;

                case 2:
                    a = _context.sent;
                    console.log(a);
                    _context.next = 6;
                    return 2;

                case 6:
                    b = _context.sent;
                    console.log(b);
                    _context.next = 10;
                    return 3;

                case 10:
                    c = _context.sent;
                    console.log(c);

                case 12:
                case "end":
                    return _context.stop();
            }
        }
    });
}

let it = read(); // 迭代器
{
    let { value, done } = it.next('xxx'); // 从实现上可以看到case 0里没有用到context.sent,所以第一个传参被忽略
    console.log(value, done); // 1 false
}
{
    let { value, done } = it.next('aaa'); // aaa
    console.log(value, done); // 2 false
}
{
    let { value, done } = it.next('bbb'); // bbb
    console.log(value, done); // 3 false
}
{
    let { value, done } = it.next('ccc'); // ccc
    console.log(value, done); // undefined true
}

四、generator+co实现异步迭代


const fs = require('fs');
const util = require('util');
// const co = require('co');
let readFile = util.promisify(fs.readFile);

function co(it) {
    return new Promise((resolve, reject) => {
        // 异步迭代用递归
        function next(data) {
            let { value, done } = it.next(data);
            if (done) {
                resolve(value);
            } else {
                Promise.resolve(value).then(next, reject);
            }
        }
        next();
    })
}

function* read() {
    let data = yield readFile('./a.txt', 'utf8');
    data = yield readFile(data, 'utf8');
    return data;
}

// 不用co
// let it = read();
// let { value, done } = it.next();
// value.then((data) => { // data => b.txt
//     let { value, done } = it.next(data);
//     value.then(data => { // data => b
//         let { value, done } = it.next(data);
//         console.log(value, done);
//     })
// })

// 用co
co(read()).then(data => {
    console.log(data); // b
}).catch(err => {
    console.log(err);
})

五、async await


asyncgenerator+ co的语法糖,await返回的是一个promise

// async await 
const fs = require('fs');
const util = require('util');
let readFile = util.promisify(fs.readFile);
async function read() {
    let data = await readFile('./a.txt', 'utf8');
    data = await readFile(data, 'utf8');
    return data;
}

read().then(data => {
    console.log(data); // b
})

六、经典面试题

Promise.resolve().then(() => {
    console.log('then1');
    return Promise.resolve().then(()=>{
        console.log('then1-1');
        return Promise.resolve();
    }).then(()=>{
        console.log('then1-2')
    })
})
.then(() => {
    console.log('then2');
})
.then(() => {
    console.log('then3');
})
.then(() => {
    console.log('then4');
})
.then(() => {
    console.log('then5');
})

then1
then1-1
then1-2
then2
then3
then4
then5

promise.resolve.thenreturnpromise,就会等待promise执行完成后,继续往下执行。

Promise.resolve().then(() => {      // ①
    console.log('then1');
    Promise.resolve().then(()=>{    // ②
        console.log('then1-1');
        return Promise.resolve();   // ④  x.then
    }).then(()=>{                   // ⑥
        console.log('then1-2')
    })
})
.then(() => {                       // ③
    console.log('then2');
})
.then(() => {                       // ⑤
    console.log('then3');
})
.then(() => {                       // ⑦
    console.log('then4');
})
.then(() => {                       // ⑧
    console.log('then5');
})

then1
then1-1
then2
then3
then4
then1-2
then5

我们先按照PromiseA+规范分析一下:
第一轮:先把promise.resolve().then的回调,即①放入执行栈。往下执行
第二轮:打印then1,将②放入微任务队列,将③放入微任务队列,执行②中promise.resolve().then,打印then1-1。执行③中then回调,打印then2
第三轮:return Promise.resolve();then中return一个promise,相当于x.then。将④放入微任务队列。接下来将⑤放入微任务队列。
第四轮:执行④,又是一个.then,所以将⑥放入微任务队列。执行⑤,打印then3
第五轮:执行⑥,打印then1-2
第六轮:⑥执行完毕后,将⑦和⑧依次放入微任务队列,依次执行。打印then4then5

我们打印的值是:then1 then1-1 then2 then3 then1-2 then4 then5
为什么和浏览器中打印表现不一致呢?因为我们是按照promiseA+规范分析的,浏览器还有自己的规定,如果return了一个promise,会额外开辟一个异步方法,相当于多了依次then。

源码及测试案例 gitee地址