node.js 22 异步 asyncaw人工智能t

本文参考原文-http://bjbsair.com/2020-03-22/tech-info/2825/

node.js 12 异步 async/await

在我的node.js系列的开篇,有介绍过node.js具有单线程,异步,非阻塞的特点。如何理解这些特点?举一个例子,如果遇到耗时操作,比如网络交互或者磁盘IO,是不是需要等待操作结束再执行下一步操作?

对于node.js来讲,属于单线程,如果需要等待操作结束再执行后面的操作就麻烦了。

node.js选择的方式是在发起一个调用后不等待结果,继续向下执行。node.js这里采用的机制是异步+回调,通过异步和回调来实现对高并发的支持

回调函数

将一个函数作为参数传递给另一个函数,并且作为参数的函数可以被执行,其本质上是一个高阶函数。

我们用之前介绍过的文件模块中的函数举例。

例如,在执行读文件操作时,可以使用readFile方法。这个方法就是用了回调函数。

fs.readFile('./test.txt', (err, data)=>{  
    if(err){  
        consolog.log(err)  
        return  
    }  
    console.log(data.toString())  
})

嵌套回调

如果我们写代码需要读取三个文件,按照以前的方式,我们无法知道哪个文件的读取先结束。也就是说,如果我们需要先读取A文件,再读取B文件,只能进行回调函数的嵌套调用。

//先读取A  
fs.readFile('./A.txt', (err, data)=>{  
    if(err){  
        consolog.log(err)  
        return  
    }  
  //再读取B  
    fs.readFile('./A.txt', (err, data)=>{  
    }  
})

如果需要读取多个文件并有明确的顺序要求,这个代码的可读性就很糟糕了。

在文件模块里,node.js还提供了另一个方法fs.readFileSync()。这是一个同步函数,可以直接得到结果。

但在业务逻辑中,面对大量的回调函数,如何进行操作呢?

使用Promise

Promise是对异步操作的封装,提供了三个状态。

  • 操作在执行中: Pending
  • 操作成功: Resolved
  • 操作失败: Rejected

从上面可以看出,pending是一个中间状态,一旦一个异步操作执行完成以后,或者转换为Resolved,或者转换为Rejected。

要使用Promise,首先需要用Promise的构造函数来封装一个现有的异步操作。我们以fs.readFile为例。

function readFilePromise(path){  
//初始化Promise  
return new Promise(function(resolve, reject){  
  fs.readFile(path, (err, data)=>{  
    if(err){  
        reject(err)  
    }else{  
      resolve(data)  
    }  
    }  
})  
}

在回调函数中,需要将不同的返回结果传入resolve或者reject中。上例中,我们将error传入reject,表示读取文件出错,将data传入resolve,表示操作成功。

对于上面封装后的Promise,调用的时候可以通过then()来获取异步操作的值,即resolve的值。通过catch()方法来reject中的错误。

promise  
.then(function(data){  
  //成功  
}).catch(function(data){  
  //出错  
});

回到之前的问题,如果需要按顺序读取三个文件A,B,C。那么此时调用前面的promise,就可以promise链式调用

readFilePromise("./A.txt").then(function(data){  
  console.log(data);  
  return readFilePromise("./B.txt");  
}).then(function(data){  
  console.log(data);  
  return readFilePromise("./C.txt");  
}).then(function(data){  
  console.log(data);  
})

看起来还是有点绕,有没有办法还是让代码实际异步执行,但程序看上去和同步一样呢?

async/await

这就是现在使用起来最为方便的async/await。程序在使用了async/await后看上去可以和同步的代码一样,可读性很强。

还是以刚才顺序读取三个文件为例。

const fs = require('fs');  
async function readData(fpath){  
  //顺序读取三个文件  
    let  fa = await fs.readFile('./A.txt');  
    let fb = await fs.readFile('./A.txt');  
    let fc = await fs.readFile('./A.txt');  
}  
readData(fpath);

上面简洁的代码可以顺序读取三个文件A,B,C。最后只需要直接调用即可。

需要注意的是,在使用async/await时,函数前面一定要写async,在函数体中,对于异步函数的调用,一定要写await。在函数外调用该函数时,直接写函数名即可。

总结

从回调函数,到Promise,再到async/await,显示了node.js异步操作的不断演化。在async/await之前还有一个过度方案generator。目前实际使用中,async/await居多。

后面会介绍node.js体系里的另一个web框架Koa,和express不一样的地方其中就有异步的处理。Koa就是使用了async/await。

如果有什么问题,欢迎大家留言讨论。

猜你喜欢

转载自blog.51cto.com/14744108/2481546
今日推荐