前端面试送命题(二)-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构造函数的参数是一个函数,我们把它称为处理器函数,处理器函数接收两个函数reslovereject作为其参数,当异步操作顺利执行则执行reslove函数, 当异步操作中发生异常时,则执行reject函数。通过resolve传入得的值,可以在then方法中获取到,通过reject传入的值可以在chatch方法中获取到。

​因为thencatch都返回一个相同的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"

 

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/11730.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论