面试官: 说说你对async的理解

大家好,我是小雨小雨,致力于分享有趣的、实用的技术文章。

内容分为翻译和原创,如果有问题,欢迎随时评论或私信,希望和大家一起进步。

分享不易,希望能够得到大家的支持和关注。

TL;DR

async是generator和promise的语法糖,利用迭代器的状态机和promise来进行自更新!

如果懒得往下看,可以看下这个极其简易版本的实现方式:

// 复制粘贴即可直接运行 function stateMac (arr) { let val; return { next(){ if ((val = arr.shift())) { return { value: val, done: false } } else { return { done: true } } } } } function asyncFn(arr) { const iterator = stateMac(arr); function doSelf () { const cur = iterator.next(); const value = cur.value; if (cur.done) { console.log('done'); return; } switch (true) { case value.then && value.toString() === '[object Promise]': value.then((result) => { console.log(result); doSelf(); }) break; case typeof value === 'function': value(); doSelf(); break; default: console.log(value); doSelf(); } } doSelf(); } const mockAsync = [ 1, new Promise((res) => { setTimeout(function () { res('promise'); }, 3000); }), function () { console.log('测试'); } ]; console.log('开始'); asyncFn(mockAsync); console.log('结束'); 前言

async & await 和我们的日常开发紧密相连,但是你真的了解其背后的原理吗?

本文假设你对promise、generator有一定了解。

简述promise

promise就是callback的另一种写法,避免了毁掉地狱,从横向改为纵向,大大提升了可读性和美观。

至于promise的实现,按照promise A+规范一点点写就好了,完成后可以使用工具进行测试,确保你的写的东西是符合规范的。

具体实现原理,市面上有各种各样的写法,我就不多此一举了。

简述generator

generator就不像promise那样,他改变了函数的执行方式。可以理解为协程,就是说多个函数互相配合完成任务。类似于这个东西:

function generator() { return { _value: [1, 2, 3, 4], next() { return { value: this._value.shift(), done: !this._value.length }; } }; } const it = generator(); console.log(it.next()); console.log(it.next()); console.log(it.next()); console.log(it.next());

这只是一个demo,仅供参考。

具体请参考.

async & await

照我的理解,其实就是generator和promise相交的产物,被解析器识别,然后转换成我们熟知的语法。

这次要做的就是去看编译之后的结果是什么样的。

既然如此,我们就带着问题去看,不然看起来也糟心不是~

async包装的函数会返回一个什么样的promise? // 源代码: async function fn() {} fn(); // 编译后变成了一大坨: // generator的polyfill require("regenerator-runtime/runtime"); 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); } } 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 fn() { return _fn.apply(this, arguments); } function _fn() { _fn = _asyncToGenerator( /*#__PURE__*/ regeneratorRuntime.mark(function _callee() { return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch ((_context.prev = _context.next)) { case 0: case "end": return _context.stop(); } } }, _callee); }) ); return _fn.apply(this, arguments); } fn();

内容也不是很多,我们一点点来看:

generator包装

fn内部调用的是_fn,一个私有方法,使用的apply绑定的this,并传入了动态参数。

_fn内调用了_asyncToGenerator方法,由于js调用栈后进先出:

读起来是这样的:fn() => _asyncToGenerator => .mark()

执行是反过来的:.mark() => _asyncToGenerator => fn()

我们先往里看,映入眼帘的是regeneratorRuntime.mark,该方法是generator的polyfill暴露的方法之一,我们去内部(require('regenerator-runtime/runtime'))简单看下这个mark是用来干什么的。

// 立即执行函数,适配commonjs和浏览器 (function (exports) { // 暴露mark方法 exports.mark = function (genFun) { // 兼容判断__proto__,处理老旧环境 if (Object.setPrototypeOf) { Object.setPrototypeOf(genFun, GeneratorFunctionPrototype); } else { genFun.__proto__ = GeneratorFunctionPrototype; // 设置Symbol.toStringTag,适配toString if (!(toStringTagSymbol in genFun)) { genFun[toStringTagSymbol] = 'GeneratorFunction'; } } // 设置原型 genFun.prototype = Object.create(Gp); return genFun; }; })(typeof module === 'Object' ? module.exports : {});

mark做了两个操作,一个是设置genFun的__proto__,一个是设置prototype,可能有人会好奇:

__proto__不是对象上的吗?prototype不是函数上的吗?为啥两个同时应用到一个上面了

这样操作是没问题的,genFun不仅是函数啊,函数还是对象,js中万物皆对象哦。你想想是不是可以通过Function构造函数new出一个函数?

然后开始设置__proto__和prototype,在次之前,我们来简单捋一下原型。

原型

下面是个人理解的一个说法,未查阅v8引擎,但是这样是说得通的。如果有问题,欢迎指出,一起沟通,我也会及时修改,以免误导他人!!!。

首先要知道这三个的概念:搞清对象的原型对象(proto)、构造函数的原型(prototype)、构造方法(constructor)。

方便记忆,只需要记住下面几条即可:

prototype是构造函数(注意:构造函数也是对象嗷)上特有的属性,代表构造函数的原型。举个例子:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjjjw.html