了解async/await

533 阅读3分钟

async和await已经出来很久了,具体什么用法也就不说了,写这篇文章的目的就是:最近我疯狂的想要了解async和await的实现方法,人在一个月里总有那么三两天想要打打渔,晒晒网,所里了解以后就想记录下来

Generator生成器

其实我们都多多少少知道,async/await其实就是Generator的语法糖,他的底层就是Generator和Promise

Generator的用法
function* gen(){
    yield 'hello';
    yield 'world';
    yield 'ends';
}
let g1 = gen()
console.log(g1)  // {}
console.log(g1.next())  // {value: 'hello', done: false}
console.log(g1.next())  // {value: 'world', done: false}
console.log(g1.next())  // {value: 'ends', done: false}
console.log(g1.next())  // {value: 'undefined', done: true}

和普通的函数的区别是:第一次调用的时候是不会立即执行的,而是指向遍历器对象,继续往下执行需要调用 next() 方法,一直执行到下一个 yield 或者 return 的地方,每次执行会返回一个对象,对象里包含 value(此次执行得到的结果,如果执行完毕就会返回undefined) 以及 done(执行的状态,如果已经执行完,就会变成true)

为什么说async/await其实就是Generator的语法糖

下面看一个例子

function getName() {
    return new Promise((rs, rj) => {
        rs('啧啧啧')
    })
}

function getAge() {
    return new Promise((rs, rj) => {
        rs('22')
    })
}
  • 使用async/await执行
async function getResult() {
    const name = await getName();
    const age = await getAge();
    return (name + age + '岁了');  // '啧啧啧22岁了'
}
getResult()
  • 使用yield
function* getResultYield() {
    const name = yield getName();
    const age = yield getAge();
    console.log('name', name)
    console.log('age', age);
    console.log(name + age + '岁了')
}
let genResult = getResultYield();
console.log('genResult', genResult);
console.log(genResult.next());
console.log(genResult.next());
console.log(genResult.next());
console.log(genResult.next());

我们会发现,其实这两中写法的区别就是把 async 换成了 * , 把await换成了yield,只不过除了这一点,还有就是我们需要自己执行 next() ,这就告诉我们,async里除了 Generator ,还需要一个 内置执行器 ,就是说让他自己执行 next()

自己实现一下吧

function* getAsyncYield() {
    const name = yield getName();
    const age = yield getAge();
    return (name + age + '岁了')
}
testAsync(getAsyncYield)
  • 根据上边的<使用yield>
function testAsync(funName) {
    // 根据上边的<let genResult = getResultYield()>得知,我们需要先得到手动的指向遍历器对象
    let genAsync = funName();
    return new Promise((rs, rj) => {
        function step(value) {
            let genItem = genAsync.next(value);
            console.log('genItem', genItem)
            if(genItem.done) {
                return rs(genItem.value)
            }
            return Promise.resolve(genItem.value).then(res => {
                console.log('res----', res)
                // rs(res)
                step(res)
            })
        }
        step()
    })
}
  • 在打印genItem的地方,在done为false的时候打印的都是 {value: Promise, done: false},这是因为 getNamegetAge 返回的都是Promise,所以在done为false的时候,需要用Promise获取到value的值,并传递到 next() 方法中返回

最后扒了一下编译完的async和await

<!--用到async/await的代码-->
async onLoad (option) {
    this.wxCode = await checkinService.wxLogin()
    const toUrl = option.toUrl ? decodeURIComponent(option.toUrl) : ''
    this.toUrl = toUrl
},
<!--编译完的代码-->
onLoad: function () {
    var _onLoad = _asyncToGenerator( /*#__PURE__*/_regenerator.default.mark(function _callee(option) {
      var toUrl;
      return _regenerator.default.wrap(function _callee$(_context) {
        while (1) {
          switch (_context.prev = _context.next) {
            case 0:
              uni.showLoading({
                title: '加载中',
                mask: true
              });
              uni.removeStorageSync('user_token');
              uni.removeStorageSync('userInformation');
              this.$store.dispatch('currentLoginState', false);
              _context.next = 6;
              return _checkin.default.wxLogin();

            case 6:
              this.wxCode = _context.sent;
              toUrl = option.toUrl ? decodeURIComponent(option.toUrl) : '';
              this.toUrl = toUrl;

            case 9:
            case "end":
              return _context.stop();
          }
        }
      }, _callee, this);
    }));

    function onLoad(_x) {
      return _onLoad.apply(this, arguments);
    }

    return onLoad;
  }()

其实我觉得重要的,和async/await有关系的只有一个 _asyncToGenerator

function _asyncToGenerator(fn) {
    return function () {
        var self = this, args = arguments;
        return new Promise(function (resolve, reject) {
            var gen = fn.apply(self, args);

            function _next(value) {
                asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
            }

            function _throw(err) {
                asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
            }

            _next(undefined);
        });
    };
}

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
    try {
        var info = gen[key](arg);
        var value = info.value;
    } catch (error) {
        reject(error);
        return;
    }
    if (info.done) {
        resolve(value);
    } else {
        Promise.resolve(value).then(_next, _throw);
    }
}

其实感觉自己写的大体上差不多,没做容错,不够完整

目前只能写到这里了,要是有啥写的不对还请指出,谢谢

还发现一个之前没注意的问题<async和await也支持异步并行>

function setA(){ 
  return new Promise((resolve)=>{ 
   setTimeout(()=>{ 
    console.log('aaa'); 
    resolve(); 
   },3000) 
  }) 
 } 
 
function setB(){ 
  return new Promise((resolve)=>{ 
   setTimeout(()=>{ 
    console.log('bbb'); 
    resolve(); 
   },3000) 
  }) 
 } 
async function testAsync(){ 
 
  // 异步  -->  等待6s 
  // let a = await setA(); 
  // let b = await setB(); 
 
  // 改成同步 --> 只等待3s 
  // let a = setA(); 
  // let b = setB(); 
  // await a; 
  // await b; 
  // console.log('all done'); 
  // 同步2 
  // Promise.all([setA(),setB()]).then(()=>{console.log('all done')}) 
 
  return '啧啧啧' 
}
 testAsync()

参考: 《async 函数的含义和用法》