美文网首页
generator和co库, async/await

generator和co库, async/await

作者: 成熟稳重的李先生 | 来源:发表于2019-12-16 00:25 被阅读0次

generator---生成器,写法和普通函数(用function关键字声明)基本一样,只是在函数名前增加了“*”号,它可以返回多次(除了最终的返回,中间的每次返回都需要“yield”关键字来标识),并且在每个阶段都可以传参,因此它可以用来优雅地替代异步回调式的代码。
先来看一个简单的generator

function * simple(){  // * 可以紧跟function,也可跟着函数名,还可以放在他俩中间
  yield 1;
  yield 2;
}
//函数simple执行后,和普通的函数不一样,它会返回一个iterator(迭代器)
let it = simple();  //这是一个迭代器。它拥有一个next方法,顾名思义,用来执行下一步。
console.log(it.next());  // {value: 1, done: false}   value:这一步得到的值,done: 是否结束
console.log(it.next());  // {value: 2, done: false}  还没有走到函数的return语句时,done总是false
// 当generator函数没有return语句时,就当它  return undefined
console.log(it.next());   // {value: undefined, done: true}

每当函数体中碰到一个“yield”时,函数就会停止,使用迭代器的next方法,才能让它进行下一步。
next方法可以接收参数,用来给这个方法的返回值赋值

function * abc(){
  let a = yield 1;
  console.log(a);
  let b = yield 2;
  console.log(b)
}
image.png
以上两处next都没有传参,因此赋值时,a和b拿到的都是undefined。还有一个点需要注意,第一次next时传参时没有必要的,因为没有变量(内存)来接收它

现在,我们来模拟一段异步代码

let fs = require("fs");
//这个函数的目的是想拿到文件的内容(假设文件名是未知,从服务器请求来的)
function* getData() {
  try {
    let fileName = yield new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve("name.txt"); // 假设有这么一个文件
      }, 1500);
    });
    let data = yield new Promise((resolve, reject) => {
      fs.readFile(fileName, "utf8", (err, data) => {
        resolve(data);
      });
    });
    return data;
  } catch (ex) {
    console.log(ex);
  }
}

let it = getData();   //生成迭代器
let { value } = it.next();  // 遇到第一个yield,停止,返回一个对象{value: promise, done: false}

value.then(data => {
  let { value } = it.next(data);  // 将promise成功的返回结果赋给fileName
  value.then(data => {
    let result = it.next(data);  // 读完文件后,将内容赋值给data
    console.log(result);
    return result;
  });
});

以上,如果层级太多还是会出现.then,.then的回调,如你所见,其实它内部都是重复的操作。有一个库会帮我们完成这些繁琐的过程 -----co
首先需要安装它,并且require

let co = require("co");
//他支持promise(thenable)的写法
//以上代码,可以这样简化
co(getData()).then(data => {   // 哇塞,清爽了很多,是不是?
  console.log(data)
})

知其然,知其所以然,因此,接下来,我们来实现一个co函数

function co(it) {
  //接收一个迭代器
  return new Promise((resolve, reject) => {
    // 他可以then,因此返回promise
    function next(val) {
      let { value, done } = it.next(val);
      if (done) {
        //结束, 将结果resolve
        resolve(value);
      } else {
        Promise.resolve(value).then(data => {
          // 首先,将value转为promise。 然后将成功的结果赋值,作为下次next的依赖
          next(data); 
        }, (err) => {
            it.throw(err);  // 如果外层有try-catch,这样写,可以被捕获到。如果用reject,那么错误将被co函数的catch接收到
        });
      }
    }
    next();
  });
}

async和await是以上的语法糖,将getData函数改为如下:

async function getData() {
  try {
    let fileName = await new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve("name.txt"); // 假设有这么一个文件
      }, 1500);
    });
    let data = await new Promise((resolve, reject) => {
      fs.readFile(fileName, "utf8", (err, data) => {
        resolve(data);
      });
    });
    console.log(data);
    return data;
  } catch (ex) {
    console.log(ex);
  }
}

getData();  //直接获取到结果了,不用再手动地next了。

接下来,我们来模拟一个依赖调用,差不多是这个样子:


GIF1.gif

从上至下,依次命名为ball1,2,3

function move(el, target) {
      return new Promise((resolve, reject) => {
        let idx = 0;

        function step() {
          idx += 3;
          if (idx < target) {
            el.style.left = idx + 'px';
            requestAnimationFrame(step)   // 原生的动画API
          } else {
            resolve(target + 100)
          }
        }
        step()
      })
    }
//基于之前说的async/await,可以这样
async function m(){
  let target = await move($('.ball1'), 200);
  let target1 = await move($('.ball2'), target);
  let target2 = await move($('.ball2'), target1);
  return target2;  // 这行无关紧要,如果不写,那么m的then函数中的成功参数就是undefined
}

m().then(data => {  //这个data就是m方法执行完毕的返回值
    console.log(data)
    alert`成功`
})

以上, 用async/await完成了一个依赖调用的例子。但是,像target1,target2这两个变量,我是为了使上下强关联才用到的,如果上一步与下一步的关系只有回调 (1走完,然后走2,没有值得依赖关系),那么这些变量可以不用,即:

async function m(){
  await move($('.ball1'), 200);
  await move($('.ball2'), 200);
  await move($('.ball2'), 200);
}

相关文章

网友评论

      本文标题:generator和co库, async/await

      本文链接:https://www.haomeiwen.com/subject/qgalnctx.html