如何支持 API 调用以使用 Promise 或带有标准 Promises 的回调,没有延迟或外部 Promise 库?
How to support API calls to use a Promise or Callbacks with Standard Promises, no defers or external Promise Library?
我想知道如何在 API 中支持 Promise
和回调。到目前为止,我已经阅读了几篇文章来尝试实现,但是所有这些文章都使用了 deferred 或具有 Deferred 的第三方 Promise 库。我想使用 Native ES Promises。我想知道如何实现这样的东西。请不要引用 Promisify 或任何第三方库,因为我实际上想要实现它。函数签名如下所示:
function (callback) {
if (callback) {
// wrap callback and return Promise thats thenable
}
// return a Promise thats thenable
}
这是我脑海中的一些事情应该如何进行,但我不确定如何实施。如果您有经验或知道如何做,请回复,我想学习。
在没有问题的进一步背景的情况下,不完全确定 "thenable" 以外的函数的预期结果是什么。
最简单的方法是利用 Promise.resolve()
或 Promise.reject()
;尽管您也可以使用 Promise.all()
、Promise.race()
或 new Promise()
构造函数来 return 一个 "thenable".
function fn(callback) {
if (callback) {
// wrap callback and return Promise thats thenable
return Promise.resolve(callback())
}
// return a Promise thats thenable
return Promise.resolve()
}
fn().then(function(data) {
console.log("no callback passed:", data); // `data` : `undefined`
})
fn(function() {return 123})
.then(function(data) {
console.log("callback passed:", data); // `data`: `123`
})
一种可能性是像 jQuery.ajax()
那样做,你总是 return 一个承诺,如果存在可选的回调,那么你也可以调用它。
当您的内部异步操作不使用承诺时,这是一个想法:
function myAPIxxx(arg1, arg2, callback) {
callback = callback || function() {};
return new Promise(function(resolve, reject) {
someAsyncOperation(arg1, arg2, function(err, data) {
if (err) {
reject(err);
callback(err);
} else {
resolve(data);
callback(null, data);
}
})
});
}
由于其中大部分是样板文件,您可以创建一个通用包装器来为所有 API 函数执行此操作。
如果你的内部操作都是用promises,那就简单一些。
function myAPIxxx(arg1, arg2, callback) {
callback = callback || function() {};
return someAsyncOperation(arg1, arg2).then(callback.bind(null, null), callback);
}
如果您已经有一个基于回调的 API 遵循 node.js 异步回调调用约定,那么您可以简单地在每个 API 调用上调用一个 promisify 函数创建一个新的基于承诺的界面。我真的不明白为什么你建议不要提及 promisify 因为如果你已经有一个基于回调的 API 并且你想支持两者,那么在你的每个 API 调用上使用 promisify 函数是最干净的,添加基于 promise 的接口的最简单方法。
如果您好奇,这里有一个 promisify 函数,它接受您现有的回调 API 函数和 return 一个新的基于 promise 的函数:
function promisify(fn, obj) {
if (typeof fn !== "function") {
throw new Error("fn argument to promisify() must be function");
}
// obj is optional and may be undefined
// if present, it will be used as context to call fn as in obj.fn()
return function(/* args */) {
// make copy of arguments object into a real array in a way that
// does not prevent interpreter optimizations
var args = new Array(arguments.length);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return new Promise(function(resolve, reject) {
// add our callback function at the end of the args list
var resultMany;
args.push(function(err, result) {
if (err) {
reject(err);
} else {
// if 0 or 1 result, then just return it as a simple value
if (arguments.length <= 2) {
resolve(result);
} else {
// if more than one result came with the callback function,
// then put it into an array so we can resolve with a single value (the array of results)
// skip the first argument which is the err value
resultMany = new Array(arguments.length - 1);
for (var i = 0; i < arguments.length - 1; i++) {
resultMany[i] = arguments[i + 1];
}
resolve(resultMany);
}
}
});
// call original function with our callback as last argument
fn.apply(obj, args);
});
}
}
因此,如果您有一个名为 myAPIxxx()
的函数,它是基于回调的,您可以创建一个新的基于承诺的函数,如下所示:
var myAPIxxxAsync = promisify(myAPIxxx);
如果您所有的 API 都是单个对象的属性,您可以只遍历 API 并通过一个循环创建现有 API 的新承诺版本。
并且,这是一个函数,它会遍历 API 对象并创建每个对象的 promisified 版本,并在它们上添加不同的后缀:
function promisfyObj(obj, suffix) {
var asyncName, method, promiseSuffix, type = typeof obj;
var hasOwn = Object.prototype.hasOwnProperty;
if (!(type === "function" || type === "object")) {
throw new Error("first argument to promisifyObj() must be function or object");
}
if (suffix && typeof suffix !== "string") {
throw new Error("second argument to promisifyObj() must be a string or not present at all");
}
promiseSuffix = suffix ? suffix : "Async";
for (method in obj) {
if (typeof obj[method] === "function" && hasOwn.call(obj, method)) {
asyncName = method + promiseSuffix;
if (!(asyncName in obj)) {
obj[asyncName] = promisify(obj[method], obj);
}
}
}
return obj;
}
所以,如果你所有的 API 函数都在一个公共对象上,例如 myAPI.someFunc()
,那么你可以像这样添加每个 API 的承诺版本:
promisifyObj(myAPI);
ES2015 中的 Promises 非常容易使用
var everyThingIsOk = true;
// here you are defining native ES2015 promise with callback function which is your async operation;
var promise = new Promise(function(resolve,reject){
if(everyThingIsOk){ // if task has done you should call resolve function with data as parameter;
resolve("this async task has been successfully done");
}
else{ // if not you should return reject function with reason that can help you;
reject("there were some errors during execution or in logic");
}
})
promise.then(function(result){ // method resolve will call callback of then
alert(result)
})
.catch(function(error){ // method reject will call callback of catch
alert(error)
})
因此对于回调,您需要方法 resolve
和 reject
;
你可以这样做
Promise.resolve(callback)
和 Promise.reject(callback)
和return这些语句来自你的函数
我想知道如何在 API 中支持 Promise
和回调。到目前为止,我已经阅读了几篇文章来尝试实现,但是所有这些文章都使用了 deferred 或具有 Deferred 的第三方 Promise 库。我想使用 Native ES Promises。我想知道如何实现这样的东西。请不要引用 Promisify 或任何第三方库,因为我实际上想要实现它。函数签名如下所示:
function (callback) {
if (callback) {
// wrap callback and return Promise thats thenable
}
// return a Promise thats thenable
}
这是我脑海中的一些事情应该如何进行,但我不确定如何实施。如果您有经验或知道如何做,请回复,我想学习。
在没有问题的进一步背景的情况下,不完全确定 "thenable" 以外的函数的预期结果是什么。
最简单的方法是利用 Promise.resolve()
或 Promise.reject()
;尽管您也可以使用 Promise.all()
、Promise.race()
或 new Promise()
构造函数来 return 一个 "thenable".
function fn(callback) {
if (callback) {
// wrap callback and return Promise thats thenable
return Promise.resolve(callback())
}
// return a Promise thats thenable
return Promise.resolve()
}
fn().then(function(data) {
console.log("no callback passed:", data); // `data` : `undefined`
})
fn(function() {return 123})
.then(function(data) {
console.log("callback passed:", data); // `data`: `123`
})
一种可能性是像 jQuery.ajax()
那样做,你总是 return 一个承诺,如果存在可选的回调,那么你也可以调用它。
当您的内部异步操作不使用承诺时,这是一个想法:
function myAPIxxx(arg1, arg2, callback) {
callback = callback || function() {};
return new Promise(function(resolve, reject) {
someAsyncOperation(arg1, arg2, function(err, data) {
if (err) {
reject(err);
callback(err);
} else {
resolve(data);
callback(null, data);
}
})
});
}
由于其中大部分是样板文件,您可以创建一个通用包装器来为所有 API 函数执行此操作。
如果你的内部操作都是用promises,那就简单一些。
function myAPIxxx(arg1, arg2, callback) {
callback = callback || function() {};
return someAsyncOperation(arg1, arg2).then(callback.bind(null, null), callback);
}
如果您已经有一个基于回调的 API 遵循 node.js 异步回调调用约定,那么您可以简单地在每个 API 调用上调用一个 promisify 函数创建一个新的基于承诺的界面。我真的不明白为什么你建议不要提及 promisify 因为如果你已经有一个基于回调的 API 并且你想支持两者,那么在你的每个 API 调用上使用 promisify 函数是最干净的,添加基于 promise 的接口的最简单方法。
如果您好奇,这里有一个 promisify 函数,它接受您现有的回调 API 函数和 return 一个新的基于 promise 的函数:
function promisify(fn, obj) {
if (typeof fn !== "function") {
throw new Error("fn argument to promisify() must be function");
}
// obj is optional and may be undefined
// if present, it will be used as context to call fn as in obj.fn()
return function(/* args */) {
// make copy of arguments object into a real array in a way that
// does not prevent interpreter optimizations
var args = new Array(arguments.length);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return new Promise(function(resolve, reject) {
// add our callback function at the end of the args list
var resultMany;
args.push(function(err, result) {
if (err) {
reject(err);
} else {
// if 0 or 1 result, then just return it as a simple value
if (arguments.length <= 2) {
resolve(result);
} else {
// if more than one result came with the callback function,
// then put it into an array so we can resolve with a single value (the array of results)
// skip the first argument which is the err value
resultMany = new Array(arguments.length - 1);
for (var i = 0; i < arguments.length - 1; i++) {
resultMany[i] = arguments[i + 1];
}
resolve(resultMany);
}
}
});
// call original function with our callback as last argument
fn.apply(obj, args);
});
}
}
因此,如果您有一个名为 myAPIxxx()
的函数,它是基于回调的,您可以创建一个新的基于承诺的函数,如下所示:
var myAPIxxxAsync = promisify(myAPIxxx);
如果您所有的 API 都是单个对象的属性,您可以只遍历 API 并通过一个循环创建现有 API 的新承诺版本。
并且,这是一个函数,它会遍历 API 对象并创建每个对象的 promisified 版本,并在它们上添加不同的后缀:
function promisfyObj(obj, suffix) {
var asyncName, method, promiseSuffix, type = typeof obj;
var hasOwn = Object.prototype.hasOwnProperty;
if (!(type === "function" || type === "object")) {
throw new Error("first argument to promisifyObj() must be function or object");
}
if (suffix && typeof suffix !== "string") {
throw new Error("second argument to promisifyObj() must be a string or not present at all");
}
promiseSuffix = suffix ? suffix : "Async";
for (method in obj) {
if (typeof obj[method] === "function" && hasOwn.call(obj, method)) {
asyncName = method + promiseSuffix;
if (!(asyncName in obj)) {
obj[asyncName] = promisify(obj[method], obj);
}
}
}
return obj;
}
所以,如果你所有的 API 函数都在一个公共对象上,例如 myAPI.someFunc()
,那么你可以像这样添加每个 API 的承诺版本:
promisifyObj(myAPI);
ES2015 中的 Promises 非常容易使用
var everyThingIsOk = true;
// here you are defining native ES2015 promise with callback function which is your async operation;
var promise = new Promise(function(resolve,reject){
if(everyThingIsOk){ // if task has done you should call resolve function with data as parameter;
resolve("this async task has been successfully done");
}
else{ // if not you should return reject function with reason that can help you;
reject("there were some errors during execution or in logic");
}
})
promise.then(function(result){ // method resolve will call callback of then
alert(result)
})
.catch(function(error){ // method reject will call callback of catch
alert(error)
})
因此对于回调,您需要方法 resolve
和 reject
;
你可以这样做
Promise.resolve(callback)
和 Promise.reject(callback)
和return这些语句来自你的函数