字如其名,把你变成Promise。

其在实际开发中的应用数不胜数,许多函数和库都是基于Callback的。但是Promise明显更加方便,结合async, await大大简化了代码。

为了更好的理解,让我们看下面的例子:

function loadScript(src, callback) {let script = document.createElement('script');script.src = src;script.onload = () => callback(null, script);script.onerror = () => callback(new Error(`Script load error for ${src}`));document.head.append(script);
}// usage:
loadScript('path/script.js', (err, script) => {...})

这是一个典型的基于Callback装载js脚本的方法,至于使用回调的drawback应该大家都耳熟能详,这里不过多赘述了。

好了,让我们Promisify它。

let loadScriptPromise = function(src) {return new Promise((resolve, reject) => {loadScript(src, (err, script) => { // the same as beforeif (err) reject(err);else resolve(script);});});
};// usage:
loadScriptPromise('path/script.js').then(...)

这里同样加载脚本,不同的是这里返回了一个Promise,参数只需要传入src,当脚本成功加载,resolve这个Promise,加载失败reject这个Promise。

但是实际的使用场景当中,我们不仅仅只promisify一个函数,我们需要一个装饰器来把它写的通用一些。

function promisify(f) {return function (...args) { // return a wrapper-function return new Promise((resolve, reject) => {function callback(err, result) { // our custom callback for f if (err) {reject(err);} else {resolve(result);}}args.push(callback); // append our custom callback to the end of f argumentsf.call(this, ...args); // call the original function});};
}// usage:
let loadScriptPromise = promisify(loadScript);
loadScriptPromise(src).then(...);

可能显得稍微有点复杂,这地方调用promisify返回一个包裹函数,这个包裹函数返回一个Promise,随后将自定义的Callback添加到args中,调用promisify传进来的函数。这样就可以promisify任何的函数了。

如果原始的f函数希望回调函数可以支持多个参数呢?形如callback(err, res1, res2, ...)?

升级一下装饰器,让他支持多个参数,这里增加一个参数,区分是否多参数。

// promisify(f, true) to get array of results
function promisify(f, manyArgs = false) {return function (...args) {return new Promise((resolve, reject) => {function callback(err, ...results) { // our custom callback for fif (err) {reject(err);} else {// resolve with all callback results if manyArgs is specifiedresolve(manyArgs ? results : results[0]);}}args.push(callback);f.call(this, ...args);});};
}// usage:
f = promisify(f, true);
f(...).then(arrayOfResults => ..., err => ...);

这样加一个参数,决定回调函数是否是多参数。

如果是定制化的Callback,类似不需要err的,可以手动promisify一下,无需装饰器。

在node中,有一个内置的util.promisify函数是特定的实现。

这里需要注意一下,promise只有一个结果,但是回调从技术层面可以被调用多次。

所以这里promisifycation只适用于回调函数调用一次的情形,更多的调用会被忽略。

附上es6-promisify源码

// Symbols is a better way to do this, but not all browsers have good support,
// so instead we'll just make do with a very unlikely string.
const customArgumentsToken = "__ES6-PROMISIFY--CUSTOM-ARGUMENTS__";/*** promisify()* Transforms callback-based function -- func(arg1, arg2 .. argN, callback) --* into an ES6-compatible Promise. Promisify provides a default callback of the* form (error, result) and rejects when `error` is truthy.** @param {function} original - The function to promisify* @return {function} A promisified version of `original`*/
export function promisify(original) {// Ensure the argument is a functionif (typeof original !== "function") {throw new TypeError("Argument to promisify must be a function");}// If the user has asked us to decode argument names for them, honour thatconst argumentNames = original[customArgumentsToken];// If the user has supplied a custom Promise implementation, use it.// Otherwise fall back to whatever we can find on the global object.const ES6Promise = promisify.Promise || Promise;// If we can find no Promise implemention, then fail now.if (typeof ES6Promise !== "function") {throw new Error("No Promise implementation found; do you need a polyfill?");}return function (...args) {return new ES6Promise((resolve, reject) => {// Append the callback bound to the contextargs.push(function callback(err, ...values) {if (err) {return reject(err);}if (values.length === 1 || !argumentNames) {return resolve(values[0]);}const o = {};values.forEach((value, index) => {const name = argumentNames[index];if (name) {o[name] = value;}});resolve(o);});// Call the function.original.apply(this, args);});};
}// Attach this symbol to the exported function, so users can use it
promisify.argumentNames = customArgumentsToken;
promisify.Promise = undefined;

--end--

Promisification相关推荐

  1. 《Node.js设计模式》高级异步准则

    本系列文章为<Node.js Design Patterns Second Edition>的原文翻译和读书笔记,在GitHub连载更新,同步翻译版链接. 欢迎关注我的专栏,之后的博文将在 ...

  2. 文件从头开始读函数_如何从头开始编写自己的Promisify函数

    文件从头开始读函数 介绍 (Introduction) In this article, you will learn how to write your own promisify function ...

  3. 【前端知识点】promise简书-30分钟带你搞懂promise面试必备

    前言 写作初衷 本书的目的是以目前还在制定中的ECMAScript 6 Promises规范为中心,着重向各位读者介绍JavaScript中对Promise相关技术的支持情况. 通过阅读本书,我们希望 ...

  4. IT:前端进阶技术路线图(初级→中级→高级)之初级(研发工具/HTML/CSS/JS/浏览器)/中级(研发链路/工程化/库/框架/性能优化/工作原理)/高级(搭建/中后台/体验管理等)之详细攻略

    IT:前端进阶技术路线图(初级→中级→高级)之初级(研发工具/HTML/CSS/JS/浏览器)/中级(研发链路/工程化/库/框架/性能优化/工作原理)/高级(搭建/Node/IDE/中后台/体验管理/ ...

  5. bluebird与原生Promise对象及bluebird模块的中文API文档

    bluebird与原生Promise对象及bluebird模块的中文API文档 2016年06月15日     9392     声明 https://itbilu.com/nodejs/npm/VJ ...

最新文章

  1. 再学 GDI+[56]: 路径 - Warp
  2. Git复习(十二)之命令专场
  3. pandas.DataFrame.iloc的使用
  4. [JSP暑假实训] 四.MyEclipse+Servlet+JSP实现火车票网站查询、修改、删除操作
  5. [centos][ntp][administrator] chrony ntp
  6. 师从施一公,4年8篇CNS,西湖大学博士后获世界最具潜力女科学家奖
  7. C语言工作笔记-函数指针的使用(补充C回调系统)
  8. 拼多多砍价小程序源码 流量主系列
  9. 区块链制作的比特币的缺陷
  10. 【1】SCN-Eureka服务注册中心
  11. mysql5.7空间运算_msyql5.7数据类型和运算符
  12. html5录音支持pc和Android、ios部分浏览器,微信也是支持的,JavaScript getUserMedia
  13. 阿里正在研发无人卡车;《王者荣耀》皮肤个性动作涉嫌抄袭致歉;​苹果泄露女生私密照赔偿数百万美元|极客头条...
  14. BZOJ 1053: [HAOI2007]反素数ant
  15. c语言编程 双斜杠报错怎么办,以双斜杠//开头的URL的含义
  16. 红米路由器ac2100怎样设置ipv6_红米路由器AC2100怎么用手机设置 | 192路由网
  17. 母牛的故事【HDOJ2018】
  18. unity hdrp的TAA
  19. Echarts地图自定义图标Symbol同时动态更改图标进行切换显示
  20. win7下maven配置+intell IDEA中多maven(导入、配置)运行综合

热门文章

  1. springboot+mysql+基于java的邮件收发管理系统 毕业设计-附源码101025
  2. 计算机怎么音乐设置,怎样设置音乐一直播放?设置音乐一直播放教程
  3. 电信物联卡:物联卡如何实名?可以应用在哪些设备?
  4. 微信小程序 php读取数据,在微信小程序中如何获取输入数据
  5. 用python画背景_如何在有背景的画布上绘制图像
  6. COPC®认证—Callnovo顾客体验运营优秀实践7.0培训圆满结业
  7. 【S32K3】MCAL和SDK环境搭建、问题描述及解决
  8. 几何学小课堂:非欧几何(广义相对论采用黎曼几何作为数学工具)【学数学关键是要学会在什么情况下,知道使用什么工具。】
  9. Unity3d入门之路-PD 过河游戏智能帮助
  10. 【后台定位】Android13后台定位方案