如何支持 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)
})

因此对于回调,您需要方法 resolvereject; 你可以这样做

Promise.resolve(callback)Promise.reject(callback)

和return这些语句来自你的函数