PS:
2018/08/08 转简体
2018/08/09 从新排版布局,代码全面使用ES6而且直接附上输出结果,补充细节
2018/08/13 补充Async..await内容,增长例子说明
2018/08/20 新增Async..await和Promise.all在遍历状况下怎么使用javascript
上文讲了关于Javascript执行机制--单线程,同异步任务,事件循环的知识点,咱们知道Javascript在某一时刻内只能执行特定的一个任务,而且会阻塞其它任务执行,为了解决这个问题,Javascript语言将任务的执行模式分红两种:html
如今咱们就讲讲关于异步编程的发展
更多细节请看阮一峰大神的《ECMAScript 6 入门》java
若是看不清晰右键图片新标签页打开node
回调函数就是一个经过函数指针调用的函数.若是你把函数的指针(地址)做为参数传递给另外一个函数,当这个指针被用来调用其所指向的函数时,咱们就说这是回调函数.回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应.
⑴定义一个回调函数;
⑵提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;
⑶当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理.
例如:git
//定义一个回调函数 function callback() { console.log('I am a callback!'); } //函数实现 function trigger(fn, time) { setTimeout(function() { fn && fn(); }, time || 1000); } //耐心等两秒哦 trigger(callback, 2000); //输出 //I am a callback!
tips:由于异步任务在执行机制里处理方式不一样的问题,try/catch语句只能捕捉执行栈上的错误,详情请回顾Javascript执行机制--单线程,同异步任务,事件循环github
也叫观察者模式,这是一种常见的编程方式,一个对象(目标对象和观察者对象)的状态发生改变,全部的依赖对象(观察者对象)都将获得通知,进行广播通知.执行事件由触发事件调用与顺序无关,易用低耦合而且不依赖函数调用.ajax
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <ul id="list"> <li id="item1">item1</li> <li id="item2">item2</li> <li id="item3">item3</li> </ul> <script type="text/javascript"> var list = document.getElementById("list"); document.addEventListener("click", function (event) { var target = event.target; if (target.nodeName == "LI") { alert(target.innerHTML); } }) </script> </body> </html>
这是一种相似事件监听可是却更强大的设计模式(不少人也把二者视为同一种模式,我的以为仍是有点区别的),例如:编程
例如咱们能够本身封装一个方法以实现更多监听事件segmentfault
var listener = (function() { //订阅队列 var list = [], //监听事件 on = function(type, fn) { if (!list[type]) list[type] = []; list[type].push(fn); }, //取消监听事件 off = function(type, fn) { list[type] = []; }, //触发事件 trigger = function() { //取出监听类型 var type = Array.prototype.shift.call(arguments), queue = list[type], i = 0, len = queue.length; for (; i < len; i++) { //带参数发布 queue[i].apply(this, arguments); } }; return { on: on, off: off, trigger: trigger, }; })(); listener.on('log', function() { console.log('I trigger a log!'); }); listener.trigger('log'); //取消以后不触发 listener.off('log'); listener.trigger('log'); //输出 //I trigger a log!
上面的方法无论怎么变化,始终避不开回调地狱的困境,直到后来被社区提出和实现而且已经被放置到ES6统一标准化的---Promise
出现.
简单来讲就两个特性:设计模式
pending
(进行中)状态只能经过异步操做的结果更改为fulfilled/resolved
(已成功)或者rejected
(已失败)的状态,即其余任何手段都不能影响到;const promise = new Promise((resolve, reject) => { true ? resolve('success') : reject('error'); }); //Promise实例 console.log(promise); promise.then( function resolved(res) { console.log(res); }, function rejected(err) { console.log(err); } ); //输出 //Promise { } //success
Promise构造函数接受一个函数做为参数,JavaScript 引擎提供两个参数分别是:
参数名 | 用途 |
---|---|
resolve | 将Promise对象的状态从pending(进行中)变为resolved(已成功) |
reject | 将Promise对象的状态从pending(进行中)变为rejected(已失败) |
其中rejected(已失败)的状态包括程序的错误也能捕捉而且不会中断程序运行,并把错误缘由当成参数传递下去
const promise = new Promise(function(resolve, reject) { //未定义变量 return num; }); promise.then(null, function rejected(err) { console.log(`rejected:${err}`); }); //输出 //rejected: ReferenceError: num is not defined
Promise 实例能访问定义在原型对象Promise.prototype
上的then
方法.它的做用是为 Promise 实例添加状态改变时的回调函数,提供两个参数分别是
参数名 | 用途 |
---|---|
resolved | resolved状态的回调函数 |
rejected(可选) | 是rejected状态的回调函数,若是发生错误但没有处理函数会程序报错而不是状态失败报错 |
const promise = new Promise(function(resolve, reject) { throw 'error'; }); //有rejected处理函数 promise.then( function resolved(res) { console.log(res); }, function rejected(err) { console.log(`捕捉错误:${err}`); } ); //没有rejected处理函数 promise.then(function resolved(res) { console.log(res); }); //捕捉错误:error //系统报错
由于then返回的是一个新的Promise实例,因此能够链式调用,若是须要把当前结果做为下一个then方法的参数将它做为返回值return.
const promise = new Promise((resolve, reject) => { true ? resolve('success') : reject('error'); }); promise .then(function resolved(res) { console.log(res); //此次不返回结果了 }) .then(function resolved(res) { console.log(res); //此次返回其余 return '成功'; }) .then( function resolved(res) { console.log(`成功信息:${res}`); }, function rejected(err) { console.log(`失败信息:${err}`); } ); //输出 //success //undefined //成功信息:成功
Promise代码量看著多只是为了让读者更直观,采用ES6箭头函数跟缩写名称能够压缩成下面这样
const promise = new Promise ((resolve, reject) => { true ? resolve ('success') : reject ('error'); }); promise.then (res => res, err => err);
then方法会隐性返回一个Promise或者开发者显性返回任何值,而Promise有本身内部机制处理:
若是回调函数中返回一个值或者不返回:
若是回调函数抛出错误:
若是回调函数返回一个Promise:
前面说过状态一旦更改是不可逆,即便以后再添加回调函数也只是返回同个结果
即便后面再抛出错误then也是返回resolved状态Promise;
const promise = new Promise ((resolve, reject) => { resolve (); throw 'error'; }); promise.then ( res => { console.log (`resloved: ${res}`); }, err => { console.log (`rejected: ${err}`); } ); //输出 //resloved: undefined
即便后面再显性返回resolved状态Promise也是返回rejected状态Promise;
const promise = new Promise ((resolve, reject) => { reject (); return Promise.resolve (); }); promise.then ( res => { console.log (`resloved: ${res}`); }, err => { console.log (`rejected: ${err}`); } ); //输出 //rejected: undefined
基本条件以下:
//开始执行时间 console.log (`开始运行时间: ${new Date ().toTimeString ()}`); const p1 = new Promise ((resolve, reject) => { //3秒后执行 setTimeout (resolve, 3000); }), p2 = new Promise ((resolve, reject) => { //1秒后执行 setTimeout (() => resolve (p1), 1000); }); //处理时间 p2.then ( res => { console.log (`p2返回成功状态时间: ${new Date ().toTimeString ()}`); }, err => { console.log (`p2返回失败状态时间: ${new Date ().toTimeString ()}`); } ); //输出 //开始运行时间: 09:27:44 GMT+0800 (中国标准时间) //p2返回成功状态时间: 09:27:47 GMT+0800 (中国标准时间)
//开始执行时间 console.log(`开始运行时间: ${new Date().toTimeString()}`); const p1 = new Promise((resolve, reject) => { //3秒后执行 setTimeout(reject, 3000); }), p2 = new Promise((resolve, reject) => { //1秒后执行 setTimeout(() => resolve(p1), 1000); }); //处理时间 p2.then( res => { console.log(`p2返回成功状态时间: ${new Date().toTimeString()}`); }, err => { console.log(`p2返回失败状态时间: ${new Date().toTimeString()}`); } ); //输出 //开始运行时间: 09:30:50 GMT+0800 (中国标准时间) //p2返回失败状态时间: 09:30:53 GMT+0800 (中国标准时间)
//开始执行时间 console.log (`开始运行时间: ${new Date ().toTimeString ()}`); const p1 = new Promise ((resolve, reject) => { //3秒后执行 setTimeout (resolve, 3000); }), p2 = new Promise ((resolve, reject) => { //1秒后执行 setTimeout (() => reject (p1), 1000); }); //处理时间 p2.then ( res => { console.log (`p2返回成功状态时间: ${new Date ().toTimeString ()}`); }, err => { console.log (`p2返回失败状态时间: ${new Date ().toTimeString ()}`); } ); //输出 //开始运行时间: 09:31:33 GMT+0800 (中国标准时间) //p2返回失败状态时间: 09:31:34 GMT+0800 (中国标准时间)
看完三个例子以后还有一个省略代码能够得出结论:
p1 | p2 | then | 间隔(s) |
---|---|---|---|
resolved | resolved | resolved | 3 |
rejectd | resolved | rejectd | 3 |
resolved | rejectd | rejectd | 1 |
rejectd | rejectd | rejectd | 1 |
结论:then回调函数的Promise状态首先取决于调用函数(p2)状态,当它resolved(已成功)状况下才取决于返回函数Promise(p1)的状态.
若是Promise状态变为rejected或者then方法运行中抛出错误,均可以用catch捕捉而且返回一个新的Promise实例,建议then方法省略错误处理,最底层添加一个catch处理机制;
const promise = new Promise((resolve, reject) => { throw 'error'; }); //省略then错误处理 promise.then().catch(res => { console.log(res); }); //输出 //error
注意:若是决定使用catch处理的话前面就不能用reject作错误处理了,由于被拦截以后是不会再通过catch了
const promise = new Promise((resolve, reject) => { throw 'error'; }); promise .then(null, err => { console.log(`then: ${err}`); return err; }) .catch(err => { //跳过 console.log(`catch: ${err}`); }) .then( res => { console.log(`resloved: ${res}`); }, err => { console.log(`rejected: ${err}`); } ); //输出 //then: error //resloved: error
从结果能够看到第一个then有了错误处理函数以后会跳过catch方法,而后第二个then会在成功处理函数里打印?说好的状态一旦更改是不可逆呢???
这里面又涉及到return值的问题了.
第一层then已经作了错误处理,返回一个resloved状态Promise;
由于catch不会处理resloved状态Promise因此跳过;
第二层then接收并在resloved处理函数处理;
若是记性好的人应该记得上面讲解例子有个相似的写法可是却依然报错,知道缘由么?
const promise = new Promise(function(resolve, reject) { throw 'error'; }); //有rejected处理函数 promise.then( function resolved(res) { console.log(res); }, function rejected(err) { console.log(`捕捉错误:${err}`); } ); //没有rejected处理函数 promise.then(function resolved(res) { console.log(res); }); //捕捉错误:error //系统报错
再重复一遍,Promise每次都会返回一个新的Promise实例,因此不能用链式调用后的结果来看待变量promise.
方法 | 做用 |
---|---|
resolve | 快速返回一个新的 Promise 对象,状态为resolved的实例 |
reject | 快速返回一个新的 Promise 对象,状态为rejected的实例 |
finally | 无论 Promise 对象最后状态如何,都会执行的操做,回调函数不接受任何参数这代表finally方法里面的操做,应该是与状态无关的,不依赖于 Promise 的执行结果。该方法是 ES2018 引入标准的,就不说了 |
The Promise.all(iterable) method returns a single Promise that resolves when all of the promises in the iterable argument have resolved or when the iterable argument contains no promises. It rejects with the reason of the first promise that rejects.
大概意思就是当迭代器参数里的全部Promise都返回成功状态或者没有Promise入参的时候返回一个成功状态Promise,不然返回第一个失败状态的Promise抛出的错误.
若是迭代器参数不是Promise也会被隐性调用Promise.resolve方法转成Promis实例.(Promise.all方法的参数必须具备 Iterator 接口,且返回的每一个成员都是 Promise 实例).
//成功 const p1 = new Promise((resolve, reject) => { resolve('suc1'); }), //成功 p2 = new Promise((resolve, reject) => { resolve('suc2'); }), //失败 p3 = new Promise((resolve, reject) => { reject('err'); }); Promise.all([p1, p2]).then(res => { console.log(`所有成功:${res}`); }); Promise.all([p1, p3]).then(null, err => console.log(`第一个失败:${err}`)); Promise.all([]).then( res => console.log('空数组返回成功Promise'), err => console.log('空数组返回失败Promise') ); //输出 //空数组返回成功Promise //所有成功: (2) ["suc1", "suc2"] //第一个失败: err
注意: 若是空数组状况下立马返回成功状态,尽管放在最后位置却第一个打印结果!
跟all相似,区别在于两点:
//成功 const p1 = new Promise((resolve, reject) => { resolve('suc1'); }), //成功 p2 = new Promise((resolve, reject) => { resolve('suc2'); }), //失败 p3 = new Promise((resolve, reject) => { reject('err'); }); Promise.race([p1, p2]).then(res => { console.log(`第一个返回成功:${res}`); }); Promise.race([p3, p2]).then(null, err => console.log(`第一个返回失败:${err}`)); Promise.race([]).then( res => console.log('空数组返回成功Promise'), err => console.log('空数组返回失败Promise') ); //输出 //第一个返回成功:suc1 //第一个返回失败:err
由于Promise的then方法会把函数结果放置到微任务队列(micro tasks),也就是当次事件循环的最后执行.若是你使用的是同步函数其实是被无故延迟执行了.若是不清楚这方面内容能够再看一下我以前写得Javascript执行机制--单线程,同异步任务,事件循环
function sync() { console.log('sync'); } function async() { setTimeout(() => console.log('async'), 1000); } Promise.resolve().then(sync); Promise.resolve().then(async); console.log('end'); //输出 //end //sync //async
实际上有两种方法能够实现让同步函数同步执行,异步函数异步执行,而且让它们具备统一的 API.
function sync() { console.log('sync'); } function async() { setTimeout(() => console.log('async'), 1000); } (() => new Promise(resolve => resolve(sync())))(); (() => new Promise(resolve => resolve(async())))(); console.log('end'); // 输出 // sync // end // async
function sync() { console.log('sync'); } function async() { setTimeout(() => console.log('async'), 1000); } (async () => sync())(); (async () => async())(); console.log('end'); // 输出 // sync // end // async
Generator 函数是 ES6 提供的一种异步编程解决方案.
语法上,首先能够把它理解成,Generator 函数是一个状态机,封装了多个内部状态.执行 Generator 函数并不执行里面程序而是返回一个遍历器对象,能够依次遍历 Generator 函数内部的每个状态.yield表达式是暂停执行的标记,而next方法能够恢复执行.
每一次调用next方法,都会返回数据结构的当前成员的信息.具体来讲,就是返回一个包含value和done两个属性的对象.其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束.
function* Generator() { yield '1'; return '2'; } const example = Generator(); console.log(example); console.log(example.next()); console.log(example.next()); // 输出 // Object [Generator] {} // { value: '1', done: false } // { value: '2', done: true }
yield表达式自己没有返回值,或者说老是返回undefined.next方法能够带一个参数,该参数就会被看成上一个yield表达式的返回值.不然上一个yield表达式的返回值维持原状不变,这个特性至关重要,咱们就依赖它从外部向内部注入不一样的值,从而调整函数行为.调用next的时候,内部指针会往下运行直到遇到下列状况:
而后咱们根据规则再去解读那一段代码的运行结果
function* foo() { let x = yield 1; console.log(`函数内部x: ${x}`); let y = x * (yield x + 2); console.log(`函数内部xy: ${x} ${y}`); return x + y; } const it = foo(); console.log(it.next(2)); console.log(it.next(3)); console.log(it.next(4)); // 输出 // 函数内部x: undefined // Object { value: 1, done: false } // 函数内部x: 3 // Object { value: 5, done: false } // 函数内部xy: 3 12 // Object { value: 15, done: true }
概念其实很清晰,过程有点复杂,你们谨记next方法能够带一个参数,该参数就会被看成上一个yield表达式的返回值.不然上一个yield表达式的返回值维持原状不变,而后根据这个规则来算就明白了.
Generator函数内部,调用另外一个Generator函数,默认状况下是没有效果的,须要用到yield表达式,带*会返回值,不带*返回遍历器对象.
function* foo() { bar(1); //内部调用Generator函数 yield bar(2); yield* bar(3); yield 1; } function* bar(num) { console.log(num); yield 2; } const it = foo(); console.log(it.next()); console.log(it.next()); console.log(it.next()); // 输出 // { value: Object [Generator] {}, done: false } // 3 // { value: 2, done: false } // { value: 1, done: false }
从输出结果可知只有yield * bar(3)生效.
若是被代理的 Generator 函数有return语句,那么就能够向代理它的Generator函数返回数据.
function* genFuncWithReturn() { yield 'a'; yield 'b'; return 'c'; } function* logReturned(genObj) { const result = yield* genObj; //返回值 console.log(result); } const it = logReturned(genFuncWithReturn()); console.log(it.next()); console.log(it.next()); console.log(it.next()); // 输出 // { value: 'a', done: false } // { value: 'b', done: false } // c // { value: undefined, done: true }
function* foo() { console.log(1); console.log(2); return; } const it = foo(); console.log(3); it.next(); // 输出 // 3 // 1 // 2
function foo() { yield 1; } const it = foo(); // 输出 // Unexpected number
function* foo() { console.log('函数内部x: ' + (yield)); yield 1; } const it = foo(); console.log(it.next()); console.log(it.next(3)); // 输出 // { value: undefined, done: false } // 函数内部x: 3 // { value: 1, done: false }
Generator函数返回的遍历器对象的throw方法(不是全局的throw方法)能够在函数体外抛出错误,这意味著出错的代码与处理错误的代码,实现了时间和空间上的分离.而后在Generator函数体内被捕获后,会附带执行下一条yield表达式.
const err = function*() { try { yield; } catch (e) { console.log(`内部捕获: ${e}`); } }; //执行 const it = err(); console.log(it.next()); //外部捕获错误 try { //两次抛出错误 console.log(it.throw('err1')); console.log(it.throw('err2')); } catch (e) { console.log(`外部捕获: ${e}`); } // 输出 // { value: undefined, done: false } // 内部捕获 err1 // { value: undefined, done: true } // 外部捕获 err2
因此这里实际执行顺序it.throw('err1') ⇨ it.next() ⇨ it.throw('err2')
同时,Generator函数体内抛出的错误,也能够被函数体外的catch捕获;
const err = function*() { yield '1'; throw 'err'; yield; yield '2'; }; //执行 const it = err(); console.log(it.next()); try { console.log(it.next()); } catch (e) { console.log('外部捕获', e); } //捕捉错误以后继续执行 console.log(it.next()); // 输出 // { value: '1', done: false } // 外部捕获 err // { value: undefined, done: true }
注意最后一次执行next函数返回{value: undefined, done: true}
,由于抛出错误后没有作内部捕获,JS引擎会认为这个Generator函数已经遍历结束.
返回给定的值或者不传默认undefined,而且终结遍历 Generator 函数.
function* Generator() { yield '1'; yield '2'; yield '3'; return '4'; } const it = Generator(); console.log(it.return(2)); console.log(it.next(3)); // 输出 // { value: 2, done: true } // { value: undefined, done: true }
//模拟ajax请求 function ajax() { return new Promise((resolve, reject) => { setTimeout( () => resolve({ abc: 123, }), 1000 ); }); } function* foo() { try { yield; } catch (err) { console.log(`内部捕获: ${err}`); } yield console.log('再次执行'); yield ajax(); return res; } const it = foo(); it.next(); it.throw('somethings happend'); it.next().value.then(res => { console.log(res); }); // 输出 // 内部捕获: somethings happend // 再次执行 // { abc: 123 }
暂停执行和恢复执行,这是Generator能封装异步任务的根本缘由,强大的错误捕捉能力能够在写法上更加自由.
咱们能够在上面的代码扩展出更多步骤,例如这种异步依赖的代码能够以同步顺序写出来,或者外层操做res1以后再传到ajax2等.
function* foo() { var res1 = yield ajax1(), res2 = yield ajax2(res1); return res; }
尽管Generator异步流程管理很是简洁,可是操做流程不算方便,须要开发者决定何时执行下一步,甚至你会发如今整个异步流程会充斥著多个next的身影.
for...of循环能够自动遍历 Generator 函数时生成的Iterator对象,一旦next方法的返回对象的done属性为true,循环就会停止,且不包含该返回对象,因此return语句会执行,但不会显示输出.
function* Generator() { yield '1'; yield '2'; yield '3'; return '4'; } for (let v of Generator()) { console.log(v); } // 输出 // 1 // 2 // 3
也能够用while实现,区别在于for...of循环返回值,while返回数据结构
function* Generator() { yield '1'; yield '2'; yield '3'; return '4'; } let it = Generator(), result = it.next(); while (!result.done) { console.log(result); result = it.next(result); } // 输出 // { value: '1', done: false } // { value: '2', done: false } // { value: '3', done: false }
上面方法不适用于异步操做,若是之间须要依赖关系的话可能会致使后续执行失败,为了实现这种效果有些很出名的衍生库co等,关键源码很短也比较简单,直接贴出来了.
/** * Execute the generator function or a generator * and return a promise. * * @param {Function} fn * @return {Promise} * @api public */ function co(gen) { var ctx = this; var args = slice.call(arguments, 1); // we wrap everything in a promise to avoid promise chaining, // which leads to memory leak errors. // see https://github.com/tj/co/issues/180 return new Promise(function(resolve, reject) { if (typeof gen === 'function') gen = gen.apply(ctx, args); if (!gen || typeof gen.next !== 'function') return resolve(gen); onFulfilled(); /** * @param {Mixed} res * @return {Promise} * @api private */ function onFulfilled(res) { var ret; try { ret = gen.next(res); } catch (e) { return reject(e); } next(ret); return null; } /** * @param {Error} err * @return {Promise} * @api private */ function onRejected(err) { var ret; try { ret = gen.throw(err); } catch (e) { return reject(e); } next(ret); } /** * Get the next value in the generator, * return a promise. * * @param {Object} ret * @return {Promise} * @api private */ function next(ret) { if (ret.done) return resolve(ret.value); var value = toPromise.call(ctx, 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,具体用法就不说了
ES2017 标准引入了 async 函数,一句话,它就是 Generator 函数的语法糖.
若是说 Promise 主要解决的是异步回调问题,那么 async + await 主要解决的就是将异步问题同步化,下降异步编程的认知负担.
function p1() { return Promise.resolve().then(res => console.log('p1')); } function p2() { return Promise.resolve().then(res => console.log('p2')); } async function Async() { console.log(1); await p1(); console.log(2); await p2(); } const it = Async().then(res => console.log(123)); // 输出 // 1 // p1 // 2 // p2 // 123
async function Async() { return await 1; } Async().then(res => console.log(res)); // 输出 // 1
async function Async() { await Promise.reject('err'); console.log('不执行了'); await Promise.resolve(); }; Async().catch(err => console.log(err)); // 输出 // err
//阻塞主线程 const delazy = (time = 2000) => new Promise(resolve => { setTimeout(() => { resolve(); }, time); }); //模拟请求 async function quest() { await delazy(); return Promise.resolve({ str: '123', }); } //统一获取 async function doSomethings() { const ary = await Promise.all([quest(), quest()]); console.log(ary.map(e => e.str)); } doSomethings();
async function Async() { try { await Promise.reject('err').then(null, err => console.log(err)); console.log('继续执行'); await Promise.resolve('suc').then(res => console.log(res)); } catch (err) {} } Async(); // 输出 // 继续执行 // suc
function p1() { return new Promise((resolve, reject) => { setTimeout(() => resolve(1), 1000); }); } function p2() { return new Promise((resolve, reject) => { setTimeout(() => resolve(2), 1000); }); } async function sum() { const [a, b] = await Promise.all([p1(), p2()]); console.log(a, b); } ==================== OR ============================ async function sum() { const a = p1(), b = p2(), c = await a, d = await b; console.log(c, d); } sum(); // 输出 // 1 2
跟上面说的co相似,将Generator函数和自动执行器,包装在一个函数里.
async function fn(args) {} //等价于 function fn(args) { return spawn(function*() {}); }
spawn函数就是自动执行器
function spawn(genF) { return new Promise(function(resolve, reject) { const gen = genF(); function step(nextF) { let next; try { next = nextF(); } catch (e) { return reject(e); } if (next.done) { return resolve(next.value); } Promise.resolve(next.value).then( function(v) { step(function() { return gen.next(v); }); }, function(e) { step(function() { return gen.throw(e); }); } ); } step(function() { return gen.next(undefined); }); }); }
代码量很少也不难,建议认真学习里面的思路