NodeJS02

Node.js的异步编程风格

Node.js是一个异步运行环境,异步意味着调用函数后结果不是立即返回,而是在未来某个时刻再通知给调用者。Node.js使用最广泛的异步编程风格是通过回调函数来实现的。Node.js可以使用以下几种异步编程风格:

  • 回调函数
  • Promise
  • async/await

回调函数是最早出现的,也是最繁琐的,多个异步操作有顺序依赖时,会产生以下的代码:

func1.success(function(){
    func2.success(function(){
        func3.success(function(){
            // do something
        });
    });
});

因此不推荐以回调函数的形式进行异步调用。

1. 回调函数

Node.js异步编程是通过回调函数实现的,但是不能说使用回调函数就异步了,如下代码就不是异步的:

function test(callback) {
    callback(1);
}

test(function(data) {
    console.log(data);
});

Node.js使用了大量的回调函数,几乎所有API都支持回调函数。由于调用接口存在失败的情况,而基于回调函数的编程无法使用标准JS中的抛出错误和捕获错误的方法,因此只能将错误对象作为回调函数的参数。Node.js中回调函数的风格是统一的,异步函数的签名如下:

func(params, callback(error, data))
  • params调用API的参数;
  • callback(error, data)回调函数。Node.js中回调函数的第一个参数永远是Error对象,之后才是回调成功的结果,如果没有出错,error=null

2. Promise

Promise对象用于表示一个异步操作的最终完成(或失败)及其结果值。一个Promise有以下几个状态:

  • pending: 初始状态;
  • fulfilled: 操作成功;
  • rejected: 操作失败。

Promise只会从pending转换为fulfilledrejected,整个转换只发生一次。Promise构造函数接收一个执行函数,该函数接收resolvereject两个回调函数,当执行函数运行成功时需调用resolve,执行错误时需调用reject

Promise构造函数的签名如下:

function Promise(function(resolve, reject): Promise{
    // 异步逻辑
});

一旦Promise发生状态变化,就会触发then方法,then方法签名如下:

Promise.prototype.then = function(onFulfilled[, onRejected]): Promise
  • onFulfilled Promise:执行成功时回调;
  • onRejected Promise:执行失败时回调,可选参数;
  • then方法:返回一个新的Promise对象,因此Promise支持链式调用。

由于then的第二个参数是可选的,因此Promise的原型提供了catch方法来捕获异步错误,catch方法签名如下:

Promise.prototype.catch = function(onRejected): Promise
  • onRejected Promise: 执行出错时回调;
  • catch方法:返回一个新的Promise对象。
基本使用
let fs = require('fs');

function readFileAsync(path){
    return new Promise(function(resolve, reject) {
        fs.readFile(path, function(err, data) {
            if (err) {
                reject(err);
                return ;
            }
            resolve(data);
        });
    });
}

readFileAsync('~/Desktop/data.txt')
    .then(function(data) {
        console.log(data);
    }).catch(function(err){
        console.log(err);
    });
链式调用

Promisethencatch回调函数的返回值会作为下一个then/catch的输入参数,因此可通过链式Promise来扁平化嵌套的回调函数。

// callback.js      回调风格
const fs = require('fs');

fs.readFile('~/Desktop/data.txt'. function(err, data){
    if (err) {
        console.log('读取data.txt出错', err);
        return ;
    }
    console.log('data.txt读取成功', data);
    fs.readFile("~/Desktop/data2.txt", function(err, data2) {
        if (err){
        console.log("读取data2.txt出错", err);
        return ;
        }
        console.log('data2.txt读取成功', data2);
    });
});
// Promise.js      Promise风格
const fs = require('fs');

function readFileAsync(path){
    return new Promise(function(resolve, reject) {
        fs.readFile(path, function(err, data) {
            if (err) {
                reject(err);
                return ;
            }
            resolve(data);
        });
    });
}

readFileAsync('~/Desktop/data.txt')
    .then(function(data){
        console.log('data.txt读取成功', data);
        return readFileAsync('~/Desktop/data2.txt');
    })
    .then(function(data2){
        console.log('data2.txt读取成功', data2);
    })
    .catch(function(err){
        console.log("读取失败", err);
    })

3. async / await

asyncawait关键字本质上是Promise的语法糖,使得能够像同步代码一样异步编写,但是不会阻塞JS线程。还是上面的例子:

// async.js 
const fs = require('fs');

function readFileAsync(path) {
    return new Promise(function(resolve, reject) {
        fs.readFile(path, function(err, data){
            if (err) {
                reject(err);
                return ;
            }
            resolve(data);
        });
    });
}

aysnc function readFiles() {
    try {
        const data1 = await readFileAsync('~/Desktop/data.txt');
        const data2 = await readFileAsync('~/Desktop/data2.txt');
        console.log('文件1内容', data1);
        console.log('文件2内容‘, data2);
    }
    catch(e) {
        console.log('读取失败', e);
    }
}

readFiles();
基本语法

(1) async
async只能放在函数声明之前,支持普通函数、箭头函数和类函数。被修饰的函数不管返回什么值,最终都会返回Promise,需要使用then才可以获得Promise的执行结果, 直接调用函数只会得到一个Promise。

  • 函数返回基本值/空/或不带then方法对象时,Promise的结果为该值,状态为fulfilled
  • 函数抛出错误时,Promise状态为rejectedreason为抛出的错误对象。
  • 函数本身返回一个Promise时,最终的Promise结果为该Promise的结果。
// 普通函数
async function doSomething(a, b) {
    return a+b;
}
// 箭头函数
const doSomething2 = async (a, b) => {
    return a + b;
};

// 类函数
class Demo {
    async test (a, b) {
        return a + b;
    }
}

const data = doSomething(1, 2);    // [object Promise]
doSomething(1, 2).then(function(result){
    console.log(result);     // 3
});

(2) await
await只能在被async修饰的函数内部调用,await可以放在任何返回Promise的函数前。在Promise执行成功的情况下,await语句将返回Promise的成功值,否则await将抛出错误,通过try/catch捕获即可。
示例:

function ajaxGet(url, timeout) {
    return new Promise(function(resolve, reject){
        const xhr = new XMLHttpRequest();
        xhr.open("GET", url, true);
        xhr.timeout = timeout;
        xhr.onload = function () {
            resolve(xhr.responseText);
        }
        xhr.onerror = function(e){
            reject(new Error("请求失败"));
        };
        xhr.ontimeout = function(){
            reject(new Error("timeout"));
        };
        xhr.send(null);
    });
}

async function getData(){
    try {
        const data = await adjaxGet('xx://a.json', 1000);   // 1秒超时
        console.log(data);
    } catch (e){
        console.error(e);
    }
}

getData();

几乎所有的callback类型的异步函数都可以使用Promise进行包装。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值