前端面试送命题(二)-callback,promise,generator,async-await

前言javascript

本篇文章适合前端架构师,或者进阶的前端开发人员;我在面试vmware前端架构师的时候,被问到关于callback,promise,generator,async-await的问题。前端

首先咱们回顾一下javascript异步的发展历程。java

ES6 之前:node

  回调函数(callback);nodejs express 中经常使用,ajax中经常使用。ios

ES6:面试

  promise对象; nodejs最先有bluebird promise的雏形,axios中经常使用。
ajax

  generator函数;nodejs koa框架使用率很高。
express

ES7:axios

  async/await语法; 当前最经常使用的异步语法,nodejs koa2 彻底使用该语法。
promise

回调函数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"
相关文章
相关标签/搜索