我想使用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”它?


当前回答

使用普通的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);
});

其他回答

我的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)。

承诺具有状态,它们开始时是待定的,可以解决:

这意味着计算成功完成。拒绝表示计算失败。

承诺返回函数不应该抛出,而是应该返回拒绝。从promise返回函数抛出将迫使您同时使用一个}catch{和一个.catch。使用promise API的人不希望抛出promise。如果您不确定异步API在JS中的工作方式,请先看这个答案。

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

因此,创建承诺通常意味着指定它们何时结算——这意味着它们何时转到已完成或已拒绝阶段,以表明数据可用(并且可以使用.then访问)。

使用支持promise构造函数的现代promise实现,如本机ES6 promise:

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

然后,您可以这样使用由此产生的承诺:

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

对于支持deferred的库(让我们在这里使用$q作为示例,但我们稍后也将使用jQuery):

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

或者使用类似jQuery的API,钩住发生一次的事件:

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

2.普通回调:

这些API很常见,因为在JS中回调很常见。让我们看看onSuccess和onFail的常见情况:

function getUserData(userId, onLoad, onFail) { …

使用支持promise构造函数的现代promise实现,如本机ES6 promise:

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)表单,它的优点是允许我们编写一个非常类似于新Promise(fn)格式的表达式,如下所示:

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

注意:这里我们利用了jQuerydeferred的解析和拒绝方法是“可分离的”这一事实;即,它们绑定到jQuery.Deferred()的实例。并非所有的库都提供此功能。

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

节点样式回调(nodebacks)具有特定的格式,其中回调总是最后一个参数,其第一个参数是错误。让我们先手动承诺:

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

To:

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

使用deferres,您可以执行以下操作(让我们在本例中使用Q,尽管Q现在支持您更喜欢的新语法):

function getStuffAsync(param) {
    var d = Q.defer();
    getStuff(param, function(err, data) {
        if (err !== null) d.reject(err);
        else d.resolve(data);
    });
    return d.promise;   
}

一般来说,您不应该手动过多地promise,大多数考虑到Node的promise库以及Node 8+中的原生promise都有一个内置的promise nodeback方法。例如

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

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

这里没有金科玉律,你一个接一个地答应他们。然而,一些promise实现允许您批量执行此操作,例如在Bluebird中,将nodeback API转换为promise API非常简单:

Promise.promisifyAll(API);

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

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

笔记:

当然,当你在.then处理程序中时,你不需要承诺。从.then处理程序返回promise将使用该promise的值进行解析或拒绝。从.then处理程序投掷也是一种很好的做法,它会拒绝承诺-这就是著名的承诺投掷安全。在实际的onload情况下,应该使用addEventListener而不是onX。

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

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

在Node.js 8中,您可以使用此npm模块动态地promisify对象方法:

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

它使用util.provify和代理,使对象保持不变。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});
  });

具有以下功能:

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

您甚至可以使用本机调用和应用来绑定某些上下文:

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

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

var request = require('request'); //http wrapped module

function requestWrapper(url, callback) {
    request.get(url, function (err, response) {
      if (err) {
        callback(err);
      }else{
        callback(null, response);             
      }      
    })
}


requestWrapper(url, function (err, response) {
    console.log(err, response)
})

转换后

var request = require('request');

function requestWrapper(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)
});