如何将现有的callbackAPI转换为承诺?

我想使用承诺,但我有一个格式的callbackAPI:

1. DOM负载或其他一次性事件:

window.onload; // set to callback ... window.onload = function(){ }; 

2.普通callback:

 function request(onChangeHandler){ ... request(function(){ // change happened }); 

3.节点样式callback(“nodeback”):

 function getStuff(dat,callback){ ... getStuff("dataParam",function(err,data){ } 

4.具有节点样式callback的整个库:

 API; API.one(function(err,data){ API.two(function(err,data2){ API.three(function(err,data3){ }) }); }); 

我如何在承诺中使用API​​,如何“promisify”呢?

承诺有国家,他们开始等待,可以解决:

  • 意味着计算成功完成。
  • 被拒绝的意思是计算失败。

承诺返回函数不应该抛出 ,他们应该返回拒绝。 从promise返回函数抛出将迫使你使用} catch { .catch 。 使用promisified APIs的人不期望承诺会抛出。 如果您不确定asynchronousAPI如何在JS中工作,请首先看到这个答案 。

1. DOM负载或其他一次性事件:

所以,创造承诺一般意味着指定何时解决 – 这意味着当他们移动到履行或拒绝阶段,以表明数据是可用的(可以用.then访问)。

使用支持Promise构造函数(如本地ES6)的现代承诺实现承诺:

 function load(){ return new Promise(function(resolve,reject){ window.onload = resolve; }); } 

然后,您将使用由此产生的承诺:

 load().then(function(){ // Do things after onload }); 

使用支持延迟的库(让我们在这个例子中使用$ q,但是我们稍后也会使用jQuery):

 function load(){ var d = $q.defer(); window.onload = function(){ d.resolve(); }; return d.promise; } 

或者像API一样使用jQuery来挂钩一个事件:

 function done(){ var d = $.Deferred(); $("#myObject").once("click",function(){ d.resolve(); }); return d.promise(); } 

2.普通callback:

这些API是相当普遍的,因为…callback在JS中很常见。 让我们看看onSuccessonFail的常见情况:

  function getUserData(userId, onLoad, onFail){ ... 

使用支持Promise构造函数(如本地ES6)的现代承诺实现承诺:

 function getUserDataAsync(userId){ return new Promise(function(resolve,reject){ getUserData(userId,resolve,reject); }); } 

使用支持延迟的库(让我们在这个例子中使用jQuery,但是我们也使用了上面的$ q):

 function getUserDataAsync(userId){ var d = $.Deferred(); getUserData(userId,function(res){ d.resolve(res); } ,function(err){ d.reject(err); }); return d.promise(); } 

jQuery还提供了一个$.Deferred(fn)表单,它的优点是允许我们编写一个非常接近new Promise(fn)表单的expression式,如下所示:

 function getUserDataAsync(userId) { return $.Deferred(function(dfrd) { getUserData(userId, dfrd.resolve, dfrd.reject); }).promise(); } 

注意:这里我们利用jQuery延迟的resolvereject方法是“可拆卸”的事实; 即。 它们绑定到jQuery.Deferred()的实例 。 并不是所有的库都提供这个function。

3.节点样式callback(“nodeback”):

节点样式callback(nodebacks)有一个特定的格式,其中callback总是最后一个参数,其第一个参数是一个错误。 我们先手动提示一个:

 getStuff("dataParam",function(err,data){ 

至:

 function getStuffAsync(param){ return new Promise(function(resolve,reject){ getStuff(param,function(err,data){ if(err !== null) return reject(err); resolve(data); }); }); } 

有了deferred,你可以做下面的事情(让我们用Q代表这个例子,尽pipeQ现在支持你应该更喜欢的新语法):

 function getStuffAsync(param){ var d = Q.defer(); getStuff(param,function(err,data){ if(err !== null) return d.reject(err); // `throw err` also works here. d.resolve(data); }); return d.promise; } 

一般来说,你不应该手动地提供过多的东西,大多数承诺在Node中devise的库以及在Node 8+中的本地promise都有一个内置的方法来提供节点。 例如

 var getStuffAsync = Promise.promisify(getStuff); // Bluebird var getStuffAsync = Q.denodeify(getStuff); // Q var getStuffAsync = util.promisify(getStuff); // Native promises, node only 

4.具有节点样式callback的整个库:

这里没有黄金法则,你们一个接一个地给予他们。 但是,有些承诺实现允许您批量执行此操作,例如在Bluebird中,将nodeback API转换为承诺API非常简单:

 Promise.promisifyAll(API); 

或者在Node中使用本地承诺:

 const promiseAPI = Object.keys(API).map(key => {key, fn: util.promisify(API[key])) .reduce((o, p) => Object.assign(o, {[p.key] : p.fn}), {}); 

笔记:

  • 当然,当你在一个处理程序中时,你不需要提出任何事情。 然后从一个处理者那里得到一个承诺.then就可以解决或拒绝这个承诺的价值。 从一个处理者投掷也是一个很好的做法,并会拒绝承诺 – 这是着名的诺言安全。
  • 在实际的onload情况下,你应该使用addEventListener而不是onX

我不认为@Benjamin的window.onloadbuild议会一直工作,因为它不检测是否在加载后调用它。 我被这么多次咬了 这是一个应该始终工作的版本:

 function promiseDOMready() { return new Promise(function(resolve) { if (document.readyState === "complete") return resolve(); document.addEventListener("DOMContentLoaded", resolve); }); } promiseDOMready().then(initOnLoad); 

今天,我可以使用Node.js中的Promise作为一个简单的Javascript方法。

Promise一个简单和基本的例子(用KISS方式):

纯Javascript JavascriptasynchronousAPI代码:

 function divisionAPI (number, divider, successCallback, errorCallback) { if (divider == 0) { return errorCallback( new Error("Division by zero") ) } successCallback( number / divider ) } 

Promise JavascriptasynchronousAPI代码:

 function divisionAPI (number, divider) { return new Promise(function (fullfilled, rejected) { if (divider == 0) { return rejected( new Error("Division by zero") ) } fullfilled( number / divider ) }) } 

(我build议访问这个美丽的来源 )

另外, Promise可以与ES7 async\await一起使用,以使程序stream等待全部结果的结果,如下所示:

 function getName () { return new Promise(function (fullfilled, rejected) { var name = "John Doe"; // wait 3000 milliseconds before calling fulfilled() method setTimeout ( function() { fullfilled( name ) }, 3000 ) }) } async function foo () { var name = await getName(); // awaits for a fullfilled result! console.log(name); // the console writes "John Doe" after 3000 milliseconds } foo() // calling the foo() method to run the code 

使用.then()方法的相同代码的另一个用法

 function getName () { return new Promise(function (fullfilled, rejected) { var name = "John Doe"; // wait 3000 milliseconds before calling fulfilled() method setTimeout ( function() { fullfilled( name ) }, 3000 ) }) } // the console writes "John Doe" after 3000 milliseconds getName().then(function(name){ console.log(name) }) 

希望这可以帮助。

在Node.js 8.0.0的候选版本中,有一个新的工具util.promisify (我已经写了关于util.promisify ),它封装了promization任何函数的能力。

与其他答案中提出的方法没有什么不同,但是具有作为核心方法的优点,并且不需要额外的依赖性。

 const fs = require('fs'); const util = require('util'); const readFile = util.promisify(fs.readFile); 

然后你有一个readFile方法返回一个原生的Promise

 readFile('./notes.txt') .then(txt => console.log(txt)) .catch(...); 

在将函数转换为promise之前在Node.JS中

 var request = require('request'); //http wrapped module function reqeustWrapper(url, callback) { request.get(url, function (err, response) { if (err) { callback(err); }else{ callback(response); } }) } requestWrapper(url,function(response){ console.log(response) }) 

转换后

 var request = require('request'); var Promise = require('bluebird'); function reqeustWrapper(url) { return new Promise(function (resolve, reject) { //returning promise request.get(url, function (err, response) { if (err) { reject(err); //promise reject }else{ resolve(response); //promise resolve } }) }) } requestWrapper('http://localhost:8080/promise_request/1').then(function(response){ console.log(response) //resolve callback(success) }).catch(function(error){ console.log(error) //reject callback(failure) }) 

因为你需要处理多个请求

 var allRequests = []; allRequests.push(requestWrapper('http://localhost:8080/promise_request/1') allRequests.push(requestWrapper('http://localhost:8080/promise_request/2') allRequests.push(requestWrapper('http://localhost:8080/promise_request/5') Promise.all(allRequests).then(function (results) { console.log(results);//result will be array which contains each promise response }).catch(function (err) { console.log(err) }); 

您可以使用JavaScript原生承诺与节点JS。

我的云9代码链接: https : //ide.c9.io/adx2803/native-promises-in-node

 /** * Created by dixit-lab on 20/6/16. */ var express = require('express'); var request = require('request'); //Simplified HTTP request client. var app = express(); function promisify(url) { return new Promise(function (resolve, reject) { request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { resolve(body); } else { reject(error); } }) }); } //get all the albums of a user who have posted post 100 app.get('/listAlbums', function (req, res) { //get the post with post id 100 promisify('http://jsonplaceholder.typicode.com/posts/100').then(function (result) { var obj = JSON.parse(result); return promisify('http://jsonplaceholder.typicode.com/users/' + obj.userId + '/albums') }) .catch(function (e) { console.log(e); }) .then(function (result) { res.end(result); }) }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("Example app listening at http://%s:%s", host, port) }) //run webservice on browser : http://localhost:8081/listAlbums 

kriskowal的Q库包括callback函数。 像这样的一个方法:

 obj.prototype.dosomething(params, cb) { ...blah blah... cb(error, results); } 

可以用Q.ninvoke转换

 Q.ninvoke(obj,"dosomething",params). then(function(results) { }); 

当你有几个函数需要callback,而你希望他们返回一个promise时,你可以使用这个函数来进行转换。

 function callbackToPromise(func){ return function(){ // change this to use what ever promise lib you are using // In this case i'm using angular $q that I exposed on a util module var defered = util.$q.defer(); var cb = (val) => { defered.resolve(val); } var args = Array.prototype.slice.call(arguments); args.push(cb); func.apply(this, args); return defered.promise; } } 

用普通的旧的香草javaScript,这是一个promisify apicallback的解决scheme。

 function get(url, callback) { var xhr = new XMLHttpRequest(); xhr.open('get', url); xhr.addEventListener('readystatechange', function () { if (xhr.readyState === 4) { if (xhr.status === 200) { console.log('successful ... should call callback ... '); callback(null, JSON.parse(xhr.responseText)); } else { console.log('error ... callback with error data ... '); callback(xhr, null); } } }); xhr.send(); } /** * @function promisify: convert api based callbacks to promises * @description takes in a factory function and promisifies it * @params {function} input function to promisify * @params {array} an array of inputs to the function to be promisified * @return {function} promisified function * */ function promisify(fn) { return function () { var args = Array.prototype.slice.call(arguments); return new Promise(function(resolve, reject) { fn.apply(null, args.concat(function (err, result) { if (err) reject(err); else resolve(result); })); }); } } var get_promisified = promisify(get); var promise = get_promisified('some_url'); promise.then(function (data) { // corresponds to the resolve function console.log('successful operation: ', data); }, function (error) { console.log(error); }); 

在build立在承诺和asynchronous的节点v7.6 +下:

 // promisify.js let promisify = fn => (...args) => new Promise((resolve, reject) => fn(...args, (err, result) => { if (err) return reject(err); return resolve(result); }) ); module.exports = promisify; 

如何使用:

 let readdir = require('fs').readdir; let promisify = require('./promisify'); let readdirP = promisify(readdir); async function myAsyncFn(path) { let entries = await readdirP(path); return entries; } 

您可以在ES6中使用原生Promise ,例如处理setTimeout:

 enqueue(data) { const queue = this; // returns the Promise return new Promise(function (resolve, reject) { setTimeout(()=> { queue.source.push(data); resolve(queue); //call native resolve when finish } , 10); // resolve() will be called in 10 ms }); } 

在这个例子中,Promise没有理由失败,所以reject()永远不会被调用。

Node.js 8.0.0包含一个新的util.promisify() API,它允许将标准的Node.jscallback风格的API封装在一个返回Promise的函数中。 下面显示了util.promisify()一个使用示例。

 const fs = require('fs'); const util = require('util'); const readfile = util.promisify(fs.readFile); readfile('/some/file') .then((data) => { /** ... **/ }) .catch((err) => { /** ... **/ }); 

请参阅改进对Promise的支持

callback样式函数总是这样的(几乎node.js中的所有函数都是这种样式):

 //fs.readdir(path[, options], callback) fs.readdir('mypath',(err,files)=>console.log(files)) 

这种风格有相同的特点:

  1. callback函数由最后一个parameter passing。

  2. callback函数总是接受错误对象作为第一个参数。

所以,你可以写一个函数来转换一个函数,像这样:

 const R =require('ramda') /** * A convenient function for handle error in callback function. * Accept two function res(resolve) and rej(reject) , * return a wrap function that accept a list arguments, * the first argument as error, if error is null, * the res function will call,else the rej function. * @param {function} res the function which will call when no error throw * @param {function} rej the function which will call when error occur * @return {function} return a function that accept a list arguments, * the first argument as error, if error is null, the res function * will call,else the rej function **/ const checkErr = (res, rej) => (err, ...data) => R.ifElse( R.propEq('err', null), R.compose( res, R.prop('data') ), R.compose( rej, R.prop('err') ) )({err, data}) /** * wrap the callback style function to Promise style function, * the callback style function must restrict by convention: * 1. the function must put the callback function where the last of arguments, * such as (arg1,arg2,arg3,arg...,callback) * 2. the callback function must call as callback(err,arg1,arg2,arg...) * @param {function} fun the callback style function to transform * @return {function} return the new function that will return a Promise, * while the origin function throw a error, the Promise will be Promise.reject(error), * while the origin function work fine, the Promise will be Promise.resolve(args: array), * the args is which callback function accept * */ const toPromise = (fun) => (...args) => new Promise( (res, rej) => R.apply( fun, R.append( checkErr(res, rej), args ) ) ) 

为了更简洁,上面的例子使用了ramda.js。 Ramda.js是function性编程的优秀库。 在上面的代码中,我们使用了它(如javascript function.prototype.apply )并追加(如javascript function.prototype.push )。 所以,我们现在可以将一个callback样式函数转换为promise样式函数:

 const {readdir} = require('fs') const readdirP = toPromise(readdir) readdir(Path) .then( (files) => console.log(files), (err) => console.log(err) ) 

toPromisecheckErr函数是由berserk库自带的,它是由ramda.js (由我创build)的函数式编程库fork。

希望这个答案对你有用。

在Node.js 8中,你可以使用这个npm模块dynamic调用对象方法:

https://www.npmjs.com/package/doasync

它使用util.promisify代理,使您的对象保持不变。 Memoization也使用WeakMaps来完成)。 这里有些例子:

与对象:

 const fs = require('fs'); const doAsync = require('doasync'); doAsync(fs).readFile('package.json', 'utf8') .then(result => { console.dir(JSON.parse(result), {colors: true}); }); 

具有function:

 doAsync(request)('http://www.google.com') .then(({body}) => { console.log(body); // ... }); 

你甚至可以使用本地callapply绑定一些上下文:

 doAsync(myFunc).apply(context, params) .then(result => { /*...*/ }); 

es6-promisify将基于callback的函数转换为基于Promise的函数。

 const promisify = require('es6-promisify'); const promisedFn = promisify(callbackedFn, args); 

参考: https : //www.npmjs.com/package/es6-promisify

您可以使用callback2Promise npm包将节点样式函数转换为Promises.`

 var c2p = require('callback2promise'); // ordinary function with any number of parameters and a callback at the end var nodeStyleFunc = function(param1, param2, callback){ setTimeout( function(){ callback(null, 'done') }, 200); } // convert the function to a promise var promise = c2p(nodeStyleFunc)(param1, param2); promise .then(result => console.log(result)) .catch(err => console.log(err));