做者:晃晃
本文原創,轉載請註明做者及出處javascript
Promise 自問世以來,獲得了大量的應用,簡直是 javascript 中的神器。它很好地解決了異步方法的回調地獄、提供了咱們在異步方法中使用 return 的能力,並將 callback 的調用歸入了本身的管理,而不是交給異步函數後咱們就無能爲力了(常常有 callback 被莫名調用兩次而致使程序出錯)。前端
今天要介紹的是 Promisify,就是回調函數與 Promise 間的橋樑。java
什麼是 promisify 呢?顧名思義,就是「promise 化」,將一個不是promise的方法變成 promise 。舉個例子:node
// 原有的callback調用 fs.readFile('test.js', function(err, data) { if (!err) { console.log(data); } else { console.log(err); } }); // promisify後 var readFileAsync = promisify(fs.readFile); readFileAsync('test.js').then(data => { console.log(data); }, err => { console.log(err); });
這兩個方法效果上是等價的,可是從掌控性來講的話,我更喜歡後面的寫法。小程序
那麼什麼樣的方法能夠經過 promisify 變成 promise 呢?這裏就須要介紹一個名詞,nodeCallback。什麼樣的 callback 叫 nodeCallback ?微信小程序
nodeCallback 有兩個條件:1. 回調函數在主函數中的參數位置必須是最後一個;2. 回調函數參數中的第一個參數必須是 error 。舉個例子:數組
// 正確 function main(a, b, c, callback) { } // 錯誤 function main(callback, a, b, c) { }
// 正確 function callback(error, result1, result2) { } // 錯誤 function callback(result1, result2, error) { }
這樣,經過 nodeCallback ,咱們定義了一個能被 promisify 的函數的格式,即,知足 nodeCallback 形式的方法,咱們能夠經過 promisify 來讓它變成一個返回 promise 的方法。promise
下面咱們來根據上述條件來手動實現一個 promisify 。微信
首先 promisify 須要返回一個 function ,而且這個 function 要返回一個 promise異步
var promisify = (func) => { return function() { var ctx = this; return new Promise(resolve => { return func.call(ctx, ...arguments); }) } }
其次,原 func 的最後一個參數是 callback
var promisify = (func) => { return function() { var ctx = this; return new Promise(resolve => { return func.call(ctx, ...arguments, function() { resolve(arguments); }); }) } }
而後,回調函數中的第一個參數是 error 標記
var promisify = (func) => { return function() { var ctx = this; return new Promise((resolve, reject) => { return func.call(ctx, ...arguments, function() { var args = Array.prototype.map.call(arguments, item => item); var err = args.shift(); if (err) { reject(err); } else { resolve(args); } }); }) } }
最後,作一些優化,好比 this 做用域的自定義、回參只有一個時不返回數組
var promisify = (func, ctx) => { // 返回一個新的function return function() { // 初始化this做用域 var ctx = ctx || this; // 新方法返回的promise return new Promise((resolve, reject) => { // 調用原來的非promise方法func,綁定做用域,傳參,以及callback(callback爲func的最後一個參數) func.call(ctx, ...arguments, function() { // 將回調函數中的的第一個參數error單獨取出 var args = Array.prototype.map.call(arguments, item => item); var err = args.shift(); // 判斷是否有error if (err) { reject(err) } else { // 沒有error則將後續參數resolve出來 args = args.length > 1 ? args : args[0]; resolve(args); } }); }) }; };
測試
// nodeCallback方法func1 var func1 = function(a, b, c, callback) { callback(null, a+b+c); } // promise化後的func2 var func2 = promisify(func1); // 調用後輸出6 func1(1, 2, 3, (err, reuslt) => { if (!err) { console.log(result); //輸出6 } }) func2(1, 2, 3).then(console.log); //輸出6
以上即是 promisify 的介紹和實現,事實上有不少用 callback 來實現異步的第三方庫提供的方法都是按照 nodeCallback 格式的,因此它們均可以經過 promisify 來讓它變成 promise ,在遇到這些方法的時候就能夠更靈活地使用啦。
iKcamp官網:http://www.ikcamp.com
訪問官網更快閱讀所有免費分享課程:《iKcamp出品|全網最新|微信小程序|基於最新版1.0開發者工具之初中級培訓教程分享》。
包含:文章、視頻、源代碼
iKcamp原創新書《移動Web前端高效開發實戰》已在亞馬遜、京東、噹噹開售。
報名地址:http://www.huodongxing.com/ev...
與
「每天練口語」
小程序總榜排名第4、教育類排名第一的研發團隊,面對面溝通交流。