从回调函数到 async

75 阅读3分钟

这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战

hi 我是小十七_,新年快乐,今天是大年初四,之前整理了从回调函数到 async 的知识,帮助大家更好的理解回调函数,分享给大家。

回调函数方式的缺点

  1. 不符合大脑正常的思维逻辑(从上到下)
  2. 使用第三方插件,会带来控制反转问题

Promise

解决了控制反转的问题

generator

解决符合大脑思维逻辑的问题

打破函数的完整运行

正常情况下,函数都会完整的自上而下运行,es6新增了一种特殊类型的函数 generator,它中间可以让步 yield,让其他的代码先执行,然后这个函数再继续运行。

// 定义一个生成器函数
function *foo(){
  console.log('111');
  yield;   // 可以做出让步
  console.log('222');
}

// 定义一个迭代器,去执行这个函数(并没有立即执行他)
var it = foo();
it.next();
console.log('333');
it.next();
// 111 333 222

1.执行 generator 的迭代器 .next() 本身会返回一个对象,对象有个 value 的属性,表示这次执行 yield 给他的之或者函数的返回值。

2.generator 和普通函数一样,都可以接收参数。

3.yield(让步)本身可以向外传递信息,也可以接收到传递的信息

function *foo(x){
  var y = x * (yield 'hello');  // yield 向外传递了一个信息
  return y;
}

var it = foo(6);
var res = it.next();
console.log(res, res.value);  // hello,收到 yield 向外传的值
var res1 = it.next(7);  // 走到 yield 暂停了,向 yield 传值
console.log(res1, res1.value);   // 42

一个迭代器本身对应一个 generator 的实例,所以两个迭代器之间的执行互不影响,除非生成器里有一些全局的变量

function *foo() {
    var x = yield 2;
    z++;
    var y = yield (x * z);
    console.log( x, y, z );
}
var z = 1;
var it1 = foo();
var it2 = foo();
var val1 = it1.next().value; // 2 <-- yield 2
var val2 = it2.next().value; // 2 <-- yield 2
val1 = it1.next( val2 * 10 ).value; // 40   <-- x:20,  z:2
val2 = it2.next( val1 * 5 ).value; // 600  <-- x:200, z:3
it1.next( val2 / 2 ); // 20 300 3
it2.next( val1 / 4 ); // 200 10 3

es6 中 for...of 可以遍历带有迭代器的数据结构,比如说数组,遍历过程中自动调用迭代器的 .next 方法(不传参数)

制造一个数字生成器,保存上一次迭代的结果

var makeup = (function(){
  var countup;
  return function(){
    if(!countup){
      countup = 1;
    }else{
      countup = countup+3;
    }
    return countup
  }
})()

console.log(makeup());  // 1
console.log(makeup());  // 4
console.log(makeup());  // 7

generator 是解决这个事的方式

function *makeup(){
  var countup;
  while(true){
    if(!countup){
      countup = 1;
    }else{
      countup = countup + 3;
    }
    yield countup;
  }
}

var it = makeup();
var res = it.next();
console.log(res.value);
var res = it.next();
console.log(res.value);
var res = it.next();
console.log(res.value);

generator 和 promise 的结合

那么,我们就需要考虑更好的写法,在解决控制反转问题的同时,代码类似于同步的写法

function foo(x, y){
  return new Promise(function(resolve, reject){
    resolve(2)
  })
}
// 主要的 generator 函数
function *main(){
  try{
    var res = yield foo(1, 3);  // yield 执行 foo 函数
  }catch(err){
    
  }
}
var it = main();
var res = it.next().value;  // 执行 generator 函数,得到 yield 返回结果,也就是 Promise 实例
res.then(function(res){
  it.next(res);  // 向 yield 传值
}, function(err){
  it.throw(res)
})

async 和 await

async 实质上就是 generator + promise

如果 await 后面跟的是 Promise,async 会自动暂停等待 Promise 决议之后,再执行接下来的语句

function foo(x, y){
  return new Promise(function(resolve, reject){
    resolve(2)
  })
}
async function main(){
  try{
    var res = await foo(1, 3); 
    console.log(res);  // 2
    console.log(333);  // 333(在后)
  }catch(err){
    
  }
}
main();

在定时器执行完成后,在执行后面的内容

function timeout(){
  return new Promise(function(resolve, reject){
    setTimeout(resolve, 10000);
  })
}
async function wait(){
  var res = await timeout();
  console.log('后面的内容'); // 10秒后打印出来
}
wait();