co模塊總體包括三部分數組
遞歸執行promisepromise
module.exports = co['default'] = co.co = co; let slice = [].slice; // 屢次用到,因此定義成變量; 原文是Array.prototype.slice // 1. 對於幾種參數類型的判斷,主要判斷是否object,array,promise,generator,generatorFunction這幾種; /** * 判斷是否爲對象 * 任何數據類型的constructor屬性返回建立該對象的函數的引用,實例的constructor屬性指向其構造函數, */ function isObject(obj) { return obj.constructor === Object; } /** * 是否promise * promise具備.then方法,.catch方法 */ function isPromise(obj) { return 'function' === typeof obj.then; //也能夠用 obj.constructor === Promise } /** * 是否generator對象, * generator具備next,throw,return方法 */ function isGenerator(obj) { return 'function' === typeof obj.next() && 'function' === typeof obj.throw() && 'function' === typeof obj.return(); } /** * generator的constructor的name爲GeneratorFunction,displayName(非標準)若是設置也爲* GeneratorFunction, * display相關參考https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/displayName *【注意】generator的constructor !== GeneratorFunction , 參考自這裏https://blog.csdn.net/sinat_30552885/article/details/85247120 */ function isGeneratorFunction(obj) { let constructor = obj.constructor; if ( !constructor ) return false; if ( constructor.name === 'GeneratorFunction' || constructor.displayName === 'GeneratorFunction') return true; return isGenerator(obj); } // 2. 將幾種不一樣的參數類型轉換爲promise // 總括 function toPromise(obj) { if (!obj) return obj; if ( isPromise(obj) ) return obj; if ( isGenerator(obj) || isGeneratorFunction(obj) ) return co.call(this, obj); // co的入參接收promise或者generator,所以若是是generator,則直接調用一遍co; if ( 'function' === typeof obj) return thunkToPromise.call(this, obj); if ( Array.isArray(obj) ) return arrayToPromise.call(this, obj); if ( isObject(obj) ) return objToPromise.call(this, obj); return obj; } /** * thunk => promise */ function thunkToPromise(fn) { let ctx = this; return new Promise(function(resolve, reject) { fn.call(ctx, function(err, res) { if ( err ) reject(err); if ( arguments.length > 2) res = slice.call(arguments,1); resolve(res); }) }) } /** * arr => promise * 將數組的每一元素變成promise,而後放入到promise.all裏統一存儲 */ function arrayToPromise(arr) { return Promise.all(arr.map(toPromise, this)); } /** * obj => promise; * obj.constructor 返回建立該對象的函數的引用 */ function objToPromise(obj) { let results = new obj.constructor(); let promises = []; let keys = Object.keys(obj); for ( let i = 0; i < keys.length; i++ ) { let key = keys[i]; let promise = toPromise(this, obj[key]); if ( promise && isPromise(promise) ) { defer(promise, key); } else { results[key] = obj[key]; } } return Promise.all(promises).then(function() { return results; }) function defer(promise, key) { promises.push(promise.then(function(res) { results[key] = res; })) } } // 3. 遞歸執行promise /** * * @param {*} fn 入參是一個promise或者generator * @return Promise; */ function co(gen) { let ctx = this; let args = slice.call(arguments, 1); return new Promise(function(resolve, reject) { if ( typeof gen === 'function') gen = gen.apply(ctx, args); if ( !gen || typeof gen !== 'function' ) resolve(gen); onFulfilled(); function onFulfilled() { let ret; try { ret = gen.next(); } catch (e) { return reject(e); } next(ret); } function onRejected() { let ret; try { ret = gen.throw(); } catch (e) { return reject(e) } next(ret); } function next(ret) { if ( ret.done ) return resolve(ret.value); let value = toPromise.call(this, ret.value); if ( value && isPromise(value)) return value.then(onFulfilled, onRejected); } return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, but the following object was passed: "' + String(ret.value) + '"')); }) } co.wrapper = function(fn) { createPromise.__generatorFunction__ = fn; return createPromise; function createPromise() { return co.call(this, fn.apply(this,arguments)) } }