我想使用promise,但我有一个回调API,格式如下:

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

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

};

2.普通回调:

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

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

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

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

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

我如何在promise中使用API,如何“promise”它?


当前回答

我的promisify版本的回调函数是P函数:

var P=函数(){var self=this;var方法=参数[0];var params=数组.原型.切片.调用(参数,1);return new Promise((resolve,reject)=>{if(method&&typeof(method)==“函数”){params.push(函数(错误,状态){if(!err)返回解析(状态)否则返回拒绝(err);});方法.应用(self,params);}否则返回拒绝(新错误(“不是函数”));});}var callback=函数(par,回调){var rnd=数学地板(Math.random()*2)+1;返回rnd>1?callback(null,par):回调(new Error(“trap”));}callback(“callback”,(err,state)=>错误?console.error(错误):console.log(状态))callback(“callback”,(err,state)=>错误?console.error(错误):console.log(状态))callback(“callback”,(err,state)=>错误?console.error(错误):console.log(状态))callback(“callback”,(err,state)=>错误?console.error(错误):console.log(状态))P(回调,“promise”).then(v=>console.log(v)).catch(e=>console.error(e))P(回调,“promise”).then(v=>console.log(v)).catch(e=>console.error(e))P(回调,“promise”).then(v=>console.log(v)).catch(e=>console.error(e))P(回调,“promise”).then(v=>console.log(v)).catch(e=>console.error(e))

P函数要求回调签名必须是回调(error,result)。

其他回答

下面是如何将函数(回调API)转换为promise的实现。

function promisify(functionToExec) {
  return function() {
    var array = Object.values(arguments);
    return new Promise((resolve, reject) => {
      array.push(resolve)
      try {
         functionToExec.apply(null, array);
      } catch (error) {
         reject(error)
      }
    })
  }
}

// USE SCENARIO

function apiFunction (path, callback) { // Not a promise
  // Logic
}

var promisedFunction = promisify(apiFunction);

promisedFunction('path').then(()=>{
  // Receive the result here (callback)
})

// Or use it with await like this
let result = await promisedFunction('path');

我认为@Benjamin的window.onload建议不会一直有效,因为它不会检测加载后是否调用。我已经被那东西咬过很多次了。以下是一个始终有效的版本:

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

承诺总是有决心和拒绝。当您编写异步包装器时,只需调用resolve即可。

您可以为几乎所有接受回调的函数编写包装函数,如下所示:

const myAsyncWrapper = (...params) =>
  new Promise((resolve, reject) => 
    someFunctionWithCallback(...params, (error, response) =>
      error ? reject(error) : resolve(response)
    )
  );

您可以进一步编写回调到promise的转换函数:

const promisify =
  (functionWithCallback) =>
  (...params) =>
    new Promise((resolve, reject) =>
      functionWithCallback(...params, (error, response) =>
        error ? reject(error) : resolve(response)
      )
    );

当使用较旧的库或SDK时,包装函数的概念尤其有用。例如,考虑FacebookGraph API的JavaScriptSDK,它使用类似的回调结构来发出API请求。

FB.api(apiURL, options, function (request) {
  if (request.error || !request) return;
  // handle request
});

在现代应用程序中,使用基于承诺的API更有用。如果您只使用一个函数一次或两次,则最好单独给出响应:

// in an async function
const response = await new Promise((resolve, reject) =>
  FB.api(apiURL, (res) => (res?.error ? reject(res?.error) : resolve(res)))
);

如果经常使用该函数,可以使用相同的包装器概念来编写如下函数:

const apiWrapper = (...params) =>
  new Promise((resolve, reject) => 
    FB.api(...params, (res) => (res?.error ? reject(res?.error) : resolve(res)))
  );

虽然promisifier有时很好,但它们不适用于这样的特定实例。在这种情况下,在Github上寻找一个现代的包装器,或者像这样编写自己的包装器。

使用普通的javaScript,这里有一个承诺api回调的解决方案。

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);
});

我通常使用的一个简单的泛型函数。

const promisify = (fn, ...args) => {
  return new Promise((resolve, reject) => {
    fn(...args, (err, data) => {
      if (err) {
        return reject(err);
      }
      resolve(data);
    });
  });
};

如何使用它

函数promify接受带有回调的函数:

   const cb = (result) => `The result is ${result}`;

   const sum = (a, b, cb) => {
    const result = a + b;
    cb(result); // passing args to the callback function
   }


  // using the util
  promise = promisify(sum, 3, 1, cb);
  promise.then(x => console.log(x)) // 4

您可能不需要这个答案,但这将有助于了解可用实用程序的内部工作原理