>[success] # 异步执行器 ~~~ 1.利用迭代器和生成器配合,做一个异步调用解决回调嵌套问题 ~~~ >[info] ## 需要知道的知识 ~~~ 1.生成器调用next 方法时候是可以传值,注意第一次调用next 方法无论传入什么参数都会被丢弃 next 传入的参数是替代上一次yield 的返回值 ,第一次执行next 前不会有任何yield 语句 2.每次执行是以yield 作为执行的分界点 ~~~ >[danger] ##### 案例 ~~~ function *test(){ const data = yield 1 console.log(data,10000); const data1 = yield 2 console.log(data1,10000); } const g = test() // 执行顺序: yield 1 console.log( g.next()) // 执行顺序 :const data =》 console.log(data,10000)=》 yield 2 // 传参的解释 :此时next传入参数100 方法的参数会替代上一次yield 返回值 即data 变成100 console.log( g.next(100)) // 执行顺序 :const data1 =》 console.log(data1,10000) // 传参的解释 :此时next传入参数300 方法的参数会替代上一次yield 返回值 即data 变成300 console.log( g.next(300)) // 打印结果 { value: 1, done: false } 100 10000 { value: 2, done: false } 300 10000 { value: undefined, done: true } ~~~ * 不传值打印 ~~~ 1.因为没有传值 所以替代变成了undefined ~~~ ~~~ function *test(){ const data = yield 1 console.log(data,10000); } const g = test() console.log( g.next()) console.log( g.next()) // 打印结果 { value: 1, done: false } undefined 10000 { value: undefined, done: true } ~~~ >[info] ## 思考模型 async 和 await ~~~ 1.下面的例子只是一个猜想并不是能运行的案例 async function a(){ const datac = await c() console.log(datac); } const ia = a() ia.next() // 第一次next 执行 await c() 此时得到返回值 // 假如await c()返回值为100我们通过next传入替代上一次datac值 // 变相我们打印datac 结果100 帮我们将这个异步过程同步 ia.next(100) ~~~ >[info] ## 利用迭代器生成器 实现思考模型中的async 和await ~~~ 1.现在有一个设想需要每一秒后打印10的递加,例如 1s 时候输出10 ,2s 时候输出20 ~~~ >[danger] ##### 最简单的方法 ~~~ 1.下面的方法形成嵌套,如果层级变多 不容易方便阅读 ~~~ ~~~ setTimeout(()=>{ console.log(10); setTimeout(()=>{ console.log(20); },1000) },1000) ~~~ >[danger] ##### 使用了async 和await ~~~ function nn (data){ return new Promise((res)=>{ setTimeout(()=>{ res(data) },1000) }) } async function test(){ const res = await nn(10) console.log(res); const res1 = await nn(20) console.log(res1); } test() ~~~ >[danger] ##### 思考使用迭代器和生成器创造一个上面类似实现模型 ~~~ 1.需要知道什么是thunk 函数,接收一定的参数,会生产出定制化的函数,最后使用定制化的函数去完成 想要实现的功能 ~~~ * 先看一个简单过渡案例 ~~~ function *gen(){ yield 1 yield ()=>{} } const g = gen(); console.log(g.next()); console.log(g.next()); // 打印结果 // { value: 1, done: false } // { value: [Function (anonymous)], done: false } ~~~ ~~~ // 创建一个thunk 函数 function fetchData(){ return (callback)=> { setTimeout(()=>{ callback(null) },1000) } } function *gen(){ const data = yield fetchData() console.log(data); const data1 = yield fetchData() console.log(data1); } const g = gen() // 此时 value 是一个函数 value() 调用这个函数 // 并且改函数 的参数是一个回调,我们利用next 传参 // 可以接受上次yield 的返回值形 g.next().value(()=>{ g.next(10).value(()=>{ g.next(20) }) }) ~~~ * 抽象成递归 ~~~ 1.在使用的时候gen方法里面不用在形成回调地狱,可以更好的按顺序去理解程序的执行 顺序,最后只要执行run 方法自然帮我们执行递归的迭代器的调用 ~~~ ~~~ // 创建一个thunk 函数 function fetchData(){ return (callback)=> { setTimeout(()=>{ callback(null) },1000) } } function *gen(){ const data = yield fetchData() console.log(data); const data1 = yield fetchData() console.log(data1); } // 抽离成递归 function run(g){ // 迭代器next 返回值结构{value:'',done:Boolean} const next = () => { let data = 0 data += 10 let res = g.next(data); if(res.done) return; res.value(next); } next(); } const g =gen() run(g) ~~~ >[info] ##### node.js readFile 利用这种形式改造 >[danger] ##### 没有改造的写法 ~~~ 1.形成嵌套 ~~~ ~~~ // nodejs 读取文件 // 这种就会形成 地狱嵌套 不停回调 fs.readFile('config.js', (err, data) => { if(err){ throw err; } // 操作data 例如需要data 在doSomethingWith 函数中操作 doSomethingWith(data) // 或者需要接着读其他文件操作 fs.readFile('config.js1',(err1,data1 )=>{ if(err1){ throw err1 } console.log(data,data1); }) }) ~~~ >[danger] ##### 改造后的写法 ~~~ const readFileThunk = (filename) => { return (callback) => { fs.readFile(filename, callback); } } const gen = function* () { const data1 = yield readFileThunk('1.txt') console.log(data1.toString()) const data2 = yield readFileThunk('2.txt') console.log(data2.toString) } let g = gen(); g.next().value((err, data1) => { g.next(data1).value((err, data2) => { g.next(data2); }) }) // 递归 function run(gen){ const next = (err, data) => { let res = gen.next(data); if(res.done) return; res.value(next); } next(); } run(g); ~~~ >[danger] ##### Generator 和 Promise 结合 ~~~ // 最后包装成 Promise 对象进行返回 const readFilePromise = (filename) => { return new Promise((resolve, reject) => { fs.readFile(filename, (err, data) => { if(err) { reject(err); }else { resolve(data); } }) }).then(res => res); } let g = gen(); // 这块和上面 thunk 的方式一样 const gen = function* () { const data1 = yield readFilePromise('1.txt') console.log(data1.toString()) const data2 = yield readFilePromise('2.txt') console.log(data2.toString) } // 这块和上面 thunk 的方式一样 function run(gen){ const next = (err, data) => { let res = gen.next(data); if(res.done) return; // 此时value 是一个promise 对象 res.value.then(next); } next(); } run(g); ~~~ >[danger] ##### 有了asyn awiter ~~~ 1.有了asyn awiter 就不用需要自己创建递归执行 方法了,可以简单看成Generator 的 * 号换成了 async,把 yield 换成了 awai ~~~ ~~~ // readFilePromise 依旧返回 Promise 对象 const readFilePromise = (filename) => { return new Promise((resolve, reject) => { fs.readFile(filename, (err, data) => { if(err) { reject(err); }else { resolve(data); } }) }).then(res => res); } // 这里把 Generator的 * 换成 async,把 yield 换成 await const gen = async function() { const data1 = await readFilePromise('1.txt') console.log(data1.toString()) const data2 = await readFilePromise('2.txt') console.log(data2.toString) } ~~~ >[info] ## 整体演变过程 ~~~ // 模拟数据请求 const query = (interval) => { return new Promise((resolve) => { setTimeout(() => { resolve(interval) }, interval) }) } // 方案一:基于PROMISE中的THEN链实现串行即可 query(1000) .then((result) => { console.log(`第一个请求成功:${result}`) return query(2000) }) .then((result) => { console.log(`第二个请求成功:${result}`) return query(3000) }) .then((result) => { console.log(`第三个请求成功:${result}`) }) // 方案二:基于generator处理 function* generator() { let result = yield query(1000) console.log(`第一个请求成功:${result}`) result = yield query(2000) console.log(`第二个请求成功:${result}`) result = yield query(3000) console.log(`第三个请求成功:${result}`) } let itor = generator() itor.next().value.then((result) => { itor.next(result).value.then((result) => { itor.next(result).value.then((result) => { itor.next(result) }) }) }) // 帮助我们把GENERATOR函数中的内容一点点去迭代执行 将上面具体化封装以下 const isPromise = function isPromise(x) { if (x !== null && /^(object|function)$/i.test(typeof x)) { if (typeof x.then === 'function') { return true } } return false } const AsyncFunction = function AsyncFunction(generator, ...params) { return new Promise(function (resolve, reject) { let itor = generator(...params) const next = (x) => { let { done, value } = itor.next(x) if (done) { resolve(value) return } if (!isPromise(value)) value = Promise.resolve(value) value.then( (result) => next(result), (reason) => reject(reason) ) } next() }) } AsyncFunction(function* generator() { let result = yield query(1000) console.log(`第一个请求成功:${result}`) result = yield query(2000) console.log(`第二个请求成功:${result}`) result = yield query(3000) console.log(`第三个请求成功:${result}`) }) .then(() => { // GENERATOR函数中的内容全部正常执行完「例如:所有请求都成功」 console.log('都成功了!!') }) .catch((reason) => { // GENERATOR函数执行中出现问题「例如某个请求失败」,则直接结束即可 console.log('请求失败', reason) }) // 方案三:AWAIT 就是GENERATOR+PROMISE的语法糖 ;(async function () { let result = await query(1000) console.log(`第一个请求成功:${result}`) result = await query(2000) console.log(`第二个请求成功:${result}`) result = await query(3000) console.log(`第三个请求成功:${result}`) })() ~~~