前端面试送命题(二)-callback,promise,generator,async-await
前言
本篇文章适合前端架构师,或者进阶的前端开发人员;我在面试vmware前端架构师的时候,被问到关于callback,promise,generator,async-await的问题。
首先我们回顾一下javascript异步的发展历程。
ES6 以前:
回调函数(callback);nodejs express 中常用,ajax中常用。
ES6:
promise对象; nodejs最早有bluebird promise的雏形,axios中常用。
generator函数;nodejs koa框架使用率很高。
ES7:
async/await语法; 当前最常用的异步语法,nodejs koa2 完全使用该语法。
回调函数callback
回调函数实际就是一个参数;将一个函数当做参数传到另一个函数里,当那个函数执行完后,再执行传进去的这个函数;这个过程就叫做回调。
回调字面也好理解,就是先处理本体函数,再处理回调的函数,举个例子,方便大家理解。
function A(callback){
    console.log("我是主体函数");
    callback();
}
function B(){
    console.log("我是回调函数");
}
A(B);
/*输出结果
我是主体函数
我是回调函数
*/
上面的例子很好理解,首先执行主体函数A,打印结果:我是主题函数;然后执行回调函数callback 也就是B,打印结果:我是回调函数。
promise对象
promise 对象用于一个异步操作的最终完成(或最终失败)及其结果的表示。
简单地说就是处理一个异步请求。我们经常会做些断言,如果我赢了你就嫁给我,如果输了我就嫁给你之类的断言。这就是promise的中文含义:断言,一个成功,一个失败。
举个例子,方便大家理解:
promise构造函数的参数是一个函数,我们把它称为处理器函数,处理器函数接收两个函数reslove和reject作为其参数,当异步操作顺利执行则执行reslove函数, 当异步操作中发生异常时,则执行reject函数。通过resolve传入得的值,可以在then方法中获取到,通过reject传入的值可以在chatch方法中获取到。
因为then和catch都返回一个相同的promise对象,所以可以进行链式调用。
function readFileByPromise("a.txt"){
    //显示返回一个promise对象
    return new Promise((resolve,reject)=>{
        fs.readFile(path,"utf8",function(err,data){
            if(err)
                reject(err);
            else
                resolve(data);
        })
    })
}
//书写方式二
readFileByPromise("a.txt").then( data =>{
    //打印文件中的内容
    console.log(data);
}).catch( error =>{
    //抛出异常,
    console.log(error);
})
generator函数
ES6的新特性generator函数(面试的时候挂在这里),中文译为生成器,在以前一个函数中的代码要么被调用,要么不被调用,还不存在能暂停的情况,generator让代码暂停成待执行,定义一个生成器很简单,在函数名前加个*号,使用上也与普通函数有区别。
举个例子,方便大家理解:
function *Calculate(a,b){
  let sum=a+b;
  console.log(sum);
  let sub=a-b;
  console.log(sub);
}
上面便是一个简单的generator声明例子。
generator函数不能直接调用,直接调用generator函数会返回一个对象,只有调用该对象的next()方法才能执行函数里的代码。
let gen=Calculate(2,7);
执行该函数:
gen.next();
/*打印
9
-5
*/
其实单独介绍generator并没有太大的价值,要配合key yield,才能真正发挥generator的价值。yield能将生Generator函数的代码逻辑分割成多个部分,下面改写上面的生成器函数。
function *Calculate(a,b){
  let sum=a+b;
  yield console.log(sum);
  let sub=a-b;
  yield console.log(sub);
}
let gen=Calculate(2,7);
gen.next();
/*输出
9*/
可以看到这段代码执行到第一个yield处就停止了,如果要让里边所有的代码都执行完就得反复调用next()方法
let gen=Calculate(2,7);
gen.next();
gen.next();
/*输出
9
-5*/
在用一个例子,来说明generator函数与回调函数的区别:
回调函数:
fs.readFile("a.txt",(err,data)=>{
    if(!err){
        console.log(data);
        fs.readFile("b.txt",(err,data)=>{
            if(!err)
                console.log(data);
        })
    }
})
这是一个典型的回调嵌套,过多的回调嵌套造成代码的可读性和可维护性大大降低,形成了令人深恶痛绝的回调地狱,试想如果有一天让你按顺序读取10个文件,那就得嵌套10层,再或者需求变更,读取顺序要变了先读b.txt,再度a.txt那改来真的不要太爽。
generator函数:
function readFile(path) {
    fs.readFile(path,"utf8",function(err,data){
          it.next(data);
    })
}
function *main() {
    var result1 = yield readFile("a.txt");
    console.log(result1);
    var result2 = yield readFile("b.txt");
    console.log(result2);
    var result3 = yield readFile("c.txt");
    console.log(result3);
}
var it = main();
it.next(); 
generator函数的强大在于允许你通过一些实现细节来将异步过程隐藏起来,依然使代码保持一个单线程、同步语法的代码风格。这样的语法使得我们能够很自然的方式表达我们程序的步骤/语句流程,而不需要同时去操作一些异步的语法格式。
async-await
async函数返回一个promise对象,如果在async函数中返回一个直接量,async会通过Promise.resolve封装成Promise对象。
我们可以通过调用promise对象的then方法,获取这个直接量。
async function test(){
    return "Hello World";
}
var result=test();
console.log(result);
//打印Promise { 'Hello World' }
那如过async函数不返回值,又会是怎么样呢?
async function test(){
}
var result=test();
console.log(result);
//打印Promise { undefined }
await会暂停当前async的执行,await会阻塞代码的执行,直到await后的表达式处理完成,代码才能继续往下执行。
await后的表达式既可以是一个Promise对象,也可以是任何要等待的值。
如果await等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
上边你看到阻塞一词,不要惊慌,async/await只是一种语法糖,代码执行与多个callback嵌套调用没有区别,本质并不是同步代码,它只是让你思考代码逻辑的时候能够以同步的思维去思考,避开回调地狱,简而言之-async/await是以同步的思维去写异步的代码,所以async/await并不会影响node的并发数,大家可以大胆的应用到项目中去!
如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
举个例子,方便大家理解:
function A() {
    return "Hello ";
}
async function B(){
    return "World";
}
async function C(){
    //等待一个字符串
    var s1=await A();
    //等待一个promise对象,await的返回值是promise对象resolve的值,也就是"World"
    var s2=await B();
    console.log(s1+s2);
}
C();
//打印"Hello World"
前端面试送命题(二)-callback,promise,generator,async-await的更多相关文章
- 【原创】分布式之数据库和缓存双写一致性方案解析(三)  前端面试送命题(二)-callback,promise,generator,async-await  JS的进阶技巧  前端面试送命题(一)-JS三座大山  Nodejs的运行原理-科普篇  优化设计提高sql类数据库的性能  简单理解token机制
		
[原创]分布式之数据库和缓存双写一致性方案解析(三) 正文 博主本来觉得,<分布式之数据库和缓存双写一致性方案解析>,一文已经十分清晰.然而这一两天,有人在微信上私聊我,觉得应该要采用 ...
 - JQuery选择器大全  前端面试送命题:面试题篇  对IOC和DI的通俗理解  c#中关于协变性和逆变性(又叫抗变)帮助理解
		
JQuery选择器大全 jQuery 的选择器可谓之强大无比,这里简单地总结一下常用的元素查找方法 $("#myELement") 选择id值等于myElement的元素 ...
 - 一个例子读懂 JS 异步编程: Callback / Promise / Generator / Async
		
JS异步编程实践理解 回顾JS异步编程方法的发展,主要有以下几种方式: Callback Promise Generator Async 需求 显示购物车商品列表的页面,用户可以勾选想要删除商品(单选 ...
 - Promise, Generator, async/await的渐进理解
		
作为前端开发者的伙伴们,肯定对Promise,Generator,async/await非常熟悉不过了.Promise绝对是烂记于心,而async/await却让使大伙们感觉到爽(原来异步可以这么简单 ...
 - Callback, Promise和Async/Await的对比
		
Callback, Promise和Async/Await的对比 Callback Hell getData1(function (data1) { console.log('我得到data1了') ...
 - js中异步方案比较完整版(callback,promise,generator,async)
		
JS 异步已经告一段落了,这里来一波小总结 1. 回调函数(callback) setTimeout(() => { // callback 函数体 }, 1000) 缺点:回调地狱,不能用 t ...
 - 前端面试送命题-JS三座大山
		
前言 本篇文章比较适合3年以上的前端工作者,JS三座大山分别指:原型与原型链,作用域及闭包,异步和单线程. 原型与原型链 说到原型,就不得不提一下构造函数,首先我们看下面一个简单的例子: functi ...
 - callback vs async.js vs promise  vs async / await
		
需求: A.依次读取 A|B|C 三个文件,如果有失败,则立即终止. B.同时读取 A|B|C 三个文件,如果有失败,则立即终止. 一.callback 需求A: let read = functio ...
 - Promise及Async/Await
		
一.为什么有Async/Await? 我们都知道已经有了Promise的解决方案了,为什么还要ES7提出新的Async/Await标准呢? 答案其实也显而易见:Promise虽然跳出了异步嵌套的怪 ...
 
随机推荐
- hdu-2018题(母牛问题)
			
HDU-2018题/*有一头母牛,它每年年初生一头小母牛.每头小母牛从第四个年头开始,每年年初也生一头小母牛.请编程实现在第n年的时候,共有多少头母牛?Input输入数据由多个测试实例组成,每个测试实 ...
 - Ehcache缓存配置以及基本使用
			
在java项目广泛的使用.它是一个开源的.设计于提高在数据从RDBMS中取出来的高花费.高延迟采取的一种缓存方案.正因为Ehcache具有健壮性(基于java开发).被认证(具有apache 2.0 ...
 - Linux/Unix 中 wheel 组的来源
			
使用过 Linux/Unix 的朋友应该知道,将用户添加都 wheel用户组,让用户可以通过在命令在前加 sudo 临时获取 root 用户的权限.但是有没有朋友会想知道为何这个用户组要交 wheel ...
 - GlusterFS 安装 on centos7
			
本文演示如何在CentOS7上安装,配置和使用GlusterFS. 1 准备工作 1.1 基础设施 编号 IP OS 主机名 角色 说明 A 192.168.1.101 CentOS7.4 ddc_n ...
 - 有效运维的 on-call 机制
			
[编者按]本文作者为云告警平台OneAlert负责人,著<云计算与OpenStack>,在IT运营管理.云计算方面从业10多年. 正文 互联网技术的发展,离不开运维支撑工作,没有零bug的 ...
 - python使用sax实现xml解析
			
之前在使用xml解析的时候,在网上搜了很多教程,最终没有能按照网上的教程实现需求. 所以呢,只好自己去看源码,在sax的__init__.py下看到这么一段代码: 1 def parse(source ...
 - [katalon] 页面切换
			
UI自动化测试过程中会涉及到需要切换多个页面, 如点击一个按钮之后跳转到新的页面, 后者A站点提交信息后,B站点审核. Katalon虽然不支持控制多个浏览器,但是支持处理tab切换. 核心方法是使用 ...
 - Orchard详解--第一篇  介绍
			
Orchard是一个开源的内容管理系统(CMS),它提供了简单的向导式的安装方法,用于快速建站(如WordPress).对于.Net的开发者来说,Orchard有更好的学习价值,所以本系列文章将对Or ...
 - EXT.NET初学
			
1.ext:Hidden 必须在body里面有ext:ResourceManager的情况下才能运行 2.ext:Store里面不能有文字
 - C++多线程同步技巧(四)--- 信号量
			
简介 信号量是维护0到指定最大值之间的同步对象.信号量状态在其计数大于0时是有信号的,而其计数是0时是无信号的.信号量对象在控制上可以支持有限数量共享资源的访问,可以用于线程同步,预防死锁等领域. 信 ...