由使用request-promise-native想到的异步处理方法

问题场景

因为js语言的特性,使用node开发程序的时候经常会遇到异步处理的问题。对于之前专长App开发的我来说,会纠结node中实现客户端API请求的“最佳实践”。下面以OAuth2.0为场景,需要处理的流程:

  1. 获取access token
  2. 使用获取到的token,发起API请求
  3. 处理API数据

处理过程

一开始,我们使用了闭包嵌套闭包的方式实现,形如:

request(options, (res, error)=>{
//handle res and error
request(options2, (res2, error2)=>{
//handle res2 and error2
})
})

我们可以允许函数的异步执行,但大多数人在思考问题的时候,尤其在解决如上的场景时,还是希望能采用线性地处理方式。于是,我们使用request-promise-native,配合aync/await,类似:

 (async ()=> {
let access = await requestpromise(authoptions).then((value)=>{
return value;
}).catch((error)=>{
return error;
});
console.log('access', access);
})();

使用async/await的时候,需要知道:

  1. await不能单独使用,其所在的上下文之前必须有async
  2. await 作用的对象是Promise对象

可以猜想 request-promise-native 必定是对request进行了Promise化,从源代码中可以看到(虽然我没看懂,应该是使用了通用的方法来创建Promise):

// Exposing the Promise capabilities
var thenExposed = false;
for ( var i = 0; i < options.expose.length; i+=1 ) {
var method = options.expose[i];
plumbing[ method === 'promise' ? 'exposePromise' : 'exposePromiseMethod' ](
options.request.Request.prototype,
null,
'_rp_promise',
method
);
if (method === 'then') {
thenExposed = true;
}
}
if (!thenExposed) {
throw new Error('Please expose "then"');
}

既然如此,我们可以构造Promise,交给await。下面就把request包裹成一个Promise:

 //token.js
module.exports.getAccessToken = async (options) => {
return new Promise(function (resolve, reject) {
request(options, function (error, res, body) {
if (!error && res.statusCode == 200) {
resolve(body);
} else {
if(error){
reject(error);
}else{
reject(body);
}
}
});
});
};
//app.js
(async ()=> {
let access = await token.getAccessToken(authoptions).then((value)=>{
//handle value if requires
return value;
}).catch((error)=>{
return error;
});
console.log('access', access);
//use token to send the request
})();

API成功返回的结果我们往往需要按需处理,这一步放在then函数中进行。因为Promise调用then仍然是Promise,因此这里链式调用的then和catch。
进一步地,我们尝试使用内置模块 util 对函数进行promise化,形如:

//token.js
const request = require('request');
const {promisify} = require('util');
const requestPromise = promisify(request);
module.exports.getAccessToken = async (options) => {
return requestPromise(options);
};
//app.js
(async ()=> {
let access = await token.getAccessToken(authoptions).then((value)=>{
//handle value if requires
return value;
}).catch((error)=>{
return error;
});
console.log('access', access);
//use token to send the request
})();

说了这么多,对我而言,目前最大的收获就是理解了如何使用Promise/async/await,把异步函数顺序执行:把带有闭包的函数包裹进Promise,然后使用async/await执行该Promise。

好了,以上是我解决此类问题的思路。我相信必然还有其他优雅的解决方式,甚至是最佳实践。今天,借此机会,抛砖引玉,希望大家能够不吝赐教。

Promise 内容复习

最后,容我温习一下Promise相关的内容,有片面的地方请大家指正。
Promise对象:

The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.

Promise有三种状态: 初始状态,执行成功,执行出错。 then()表示promise执行后的进一步处理,它可以带两个callback参数:第一个用于promise成功运行后执行,第二个表示promise运行失败后执行。catch()表示promise运行失败后所执行的工作。catch()可以理解为语法糖,当then()的第二个callback参数省略的时候,意味着需要调用catch(因为未处理的失败的promise在将来某个node版本会导致程序退出)。需要注意的是,then()/catch()方法也是返回Promise,因此可以链式调用。

参考

Promise-MDN web docs
用图表和实例解释 Await 和 Async

javascript 学习: async await

由使用request-promise-native想到的异步处理方法的更多相关文章

  1. 微信小程序:封装全局的promise异步调用方法

    微信小程序:封装全局的promise异步调用方法 一:封装 function POST(url, params) { let promise = new Promise(function (resol ...

  2. 你所必须掌握的三种异步编程方法callbacks,listeners,promise

    目录: 前言 Callbacks Listeners Promise 前言 coder都知道,javascript语言运行环境是单线程的,这意味着任何两行代码都不能同时运行.多任务同时进行时,实质上形 ...

  3. Promise是如何实现异步编程的?

    Promise标准 不能免俗地贴个Promise标准链接Promises/A+.ES6的Promise有很多方法,包括Promise.all()/Promise.resolve()/Promise.r ...

  4. 小程序使用 Promise.all 完成文件异步上传

    小程序使用 Promise.all 完成文件异步上传 extends [微信小程序开发技巧总结(二) -- 文件的选取.移动.上传和下载 - Kindear - 博客园 (cnblogs.com)] ...

  5. axios浏览器异步请求方法封装 XMLHttpRequest

    axios学习笔记defaults(浏览器端异步请求处理方式) 浏览器异步请求方法封装,主要使用XMLHttpRequest lib/adapters/xhr.js //入口 var utils = ...

  6. jquery ajax success 函数 异步调用方法中不能给全局变量赋值的原因及解决办法

    jquery ajax success 函数 异步调用方法中不能给全局变量赋值的原因及解决办法   在调用一个jquery的ajax方法时我们有时会需要该方法返回一个值或者给某个全局变量赋值,可是我们 ...

  7. 使用AFNetworking 2.0 请求数据时出现错误 Request failed: unacceptable content-type: text/html 解决方法

    使用AFNetworking 2.0 请求数据时出现错误 Request failed: unacceptable content-type: text/html 解决方法 添加一行 manager. ...

  8. nginx 出现413 Request Entity Too Large问题的解决方法

    nginx 出现413 Request Entity Too Large问题的解决方法 使用php上传图片(大小1.9M),出现 nginx: 413 Request Entity Too Large ...

  9. php异步调用方法实现示例

    php 异步调用方法   客户端与服务器端是通过HTTP协议进行连接通讯,客户端发起请求,服务器端接收到请求后执行处理,并返回处理结果.   有时服务器需要执行很耗时的操作,这个操作的结果并不需要返回 ...

随机推荐

  1. JS上传图片转化成Base64编码demo

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  2. Oracle PL/SQL Dev工具(破解版)被植入勒索病毒的安全预警及自查通告

    [问题描述] 近日,有项目组遇到了勒索软件攻击:勒索代码隐藏在Oracle PL/SQL Dev软件中(网上下载的破解版),里面的一个文件afterconnet.sql被黑客注入了病毒代码.这个代码会 ...

  3. libcrypto.so.10内容丢失导致sshd无法运行

    今天启动虚拟机,ssh服务起不来,提示如下: Starting sshd: /usr/sbin/sshd: error while loading shared libraries: libcrypt ...

  4. 存储过程存储过程需要用两个'',先where再Group,再Order by

    存储过程需要用两个'',先where再Group,再Order by  未完,待续

  5. Oracle SQL语句操作数字:取整、四舍五入及格式化

    用oracle sql对数字进行操作: 取上取整.向下取整.保留N位小数.四舍五入.数字格式化 取整(向下取整): select floor(5.534) from dual;select trunc ...

  6. 沉淀再出发:在python3中导入自定义的包

    沉淀再出发:在python3中导入自定义的包 一.前言 在python中如果要使用自己的定义的包,还是有一些需要注意的事项的,这里简单记录一下. 二.在python3中导入自定义的包 2.1.什么是模 ...

  7. ZT Android4.2关于bluetooth在HAL层的分析(1)

    我的电子杂烩饭 http://blog.sina.com.cn/wuchuchu2012 [订阅][手机订阅] 首页 博文目录 图片 关于我 正文 字体大小:大 中 小 Android4.2关于blu ...

  8. [BZOJ 3441]乌鸦喝水

    3441: 乌鸦喝水 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 374  Solved: 148[Submit][Status][Discuss] ...

  9. 串口编程 System.IO.Ports.SerialPort类

    从Microsoft .Net 2.0版本以后,就默认提供了System.IO.Ports.SerialPort类,用户可以非常简单地编写少量代码就完成串口的信息收发程序.本文将介绍如何在PC端用C# ...

  10. Linux基础入门 - 3

    第四节 Linux 目录结构及文件基本操作 4-1.Linux目录结构 Linux 的目录与 Windows 的目录的实现机制是完全不同的.一种不同是体现在目录与存储介质(磁盘,内存,DVD 等)的关 ...