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


当前回答

es6 promisify将基于回调的函数转换为基于Promise的函数。

const promisify = require('es6-promisify');

const promisedFn = promisify(callbackedFn, args);

裁判:https://www.npmjs.com/package/es6-promisify

其他回答

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

死灵一点,这个链接可能有用。。。。


太长,读不下去了看看这个答案末尾的代码片段示例


编写/转换可调用的函数

cb(错误,结果)或新Promise(…)格式


promiseToCB转换并导出先前编码为返回promise的现有函数cbToPromise转换并导出一个先前编码为调用最后一个参数的现有函数(error,result)如果包装函数提供了多个结果,则结果将是一个结果数组例如cb(undefined,path,stat)--->解析([path,stat])/cb(undefine,[path,stat])asPromise允许您编写一个新函数以返回promise,但它可以以任何方式调用asCallback允许您编写一个新函数来调用cb(err,result),但它可以通过任何方式调用

示例函数

每个样本取2个参数,并基于随机数解决/拒绝/错误。

arg2也可以用于强制通过或失败。(查找“-通过”或“-失败”)。

包装现有函数

将函数导出到当前的“this”(或使用promiseToCB(函数myFunc(){},newThis);)



    promiseToCB(function sampleFunc1(arg1,arg2) {
        console.log("deciding:",arg1,arg2);
        return new Promise(function(resolve,reject){
       
           const timer = setTimeout(function(){reject([arg1,arg2,"ouch"].join("-"));},5000);
           
           setTimeout(function(){
               if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
    
                   console.log("complete:",arg1,arg2);
                   clearTimeout(timer);
                   resolve([arg1,arg2,"all good"].join("-"));
               }
           },2000);
        
        });
    });
    
    cbToPromise('sampleFunc2',function someOtherName(arg1,arg2,cb) {
       console.log("deciding:",arg1,arg2);
       const timer = setTimeout(function(){cb([arg1,arg2,"ouch"].join("-"));},5000);
       
       setTimeout(function(){
           if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
               console.log("complete:",arg1,arg2);
               clearTimeout(timer);
               cb(undefined,[arg1,arg2,"all good"].join("-"));
           }
       },2000);
        
    },local);
    

或者编写嵌入包装器的新函数。

     function sampleFunc3(arg1,arg2) {return asPromise(arguments,function(resolve,reject){
       console.log("deciding:",arg1,arg2);
       const timer = setTimeout(function(){reject([arg1,arg2,"ouch"].join("-"));},5000);
       
       setTimeout(function(){
           if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
               console.log("complete:",arg1,arg2);
               clearTimeout(timer);
               resolve([arg1,arg2,"all good"].join("-"));
           }
       },2000);
        
    });}
    
    function sampleFunc4(arg1,arg2) {return asCallback(arguments,function(cb){
       console.log("deciding:",arg1,arg2);
       const timer = setTimeout(function(){cb([arg1,arg2,"ouch"].join("-"));},5000);
       
       setTimeout(function(){
           if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
               console.log("complete:",arg1,arg2);
               clearTimeout(timer);
               cb(undefined,[arg1,arg2,"all good"].join("-"));
           }
       },2000);
        
    });}

测试上述功能的scipt


    const local = {}; 
    promiseToCB(function sampleFunc1(arg1,arg2) {
        console.log("deciding:",arg1,arg2);
        return new Promise(function(resolve,reject){
       
           const timer = setTimeout(function(){reject([arg1,arg2,"ouch"].join("-"));},5000);
           
           setTimeout(function(){
               if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
    
                   console.log("complete:",arg1,arg2);
                   clearTimeout(timer);
                   resolve([arg1,arg2,"all good"].join("-"));
               }
           },2000);
        
        });
    });
    
    cbToPromise('sampleFunc2',function someOtherName(arg1,arg2,cb) {
       console.log("deciding:",arg1,arg2);
       const timer = setTimeout(function(){cb([arg1,arg2,"ouch"].join("-"));},5000);
       
       setTimeout(function(){
           if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
               console.log("complete:",arg1,arg2);
               clearTimeout(timer);
               cb(undefined,[arg1,arg2,"all good"].join("-"));
           }
       },2000);
        
    },local);
    
    function sampleFunc3(arg1,arg2) {return asPromise(arguments,function(resolve,reject){
       console.log("deciding:",arg1,arg2);
       const timer = setTimeout(function(){reject([arg1,arg2,"ouch"].join("-"));},5000);
       
       setTimeout(function(){
           if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
               console.log("complete:",arg1,arg2);
               clearTimeout(timer);
               resolve([arg1,arg2,"all good"].join("-"));
           }
       },2000);
        
    });}
    
    function sampleFunc4(arg1,arg2) {return asCallback(arguments,function(cb){
       console.log("deciding:",arg1,arg2);
       const timer = setTimeout(function(){cb([arg1,arg2,"ouch"].join("-"));},5000);
       
       setTimeout(function(){
           if (arg2.endsWith("-pass") || (!arg2.endsWith("-fail") && Math.random()<0.5)) {
               console.log("complete:",arg1,arg2);
               clearTimeout(timer);
               cb(undefined,[arg1,arg2,"all good"].join("-"));
           }
       },2000);
        
    });}
    
    const log=console.log.bind(console),info=console.info.bind(console),error=console.error.bind(console);
    
    sampleFunc1("sample1","promise").then (log).catch(error);
    local.sampleFunc2("sample2","promise").then (log).catch(error);
    sampleFunc3("sample3","promise").then (log).catch(error);
    sampleFunc4("sample4","promise").then (log).catch(error);

    sampleFunc1("sample1","callback",info);
    local.sampleFunc2("sample2","callback",info);
    sampleFunc3("sample3","callback",info);
    sampleFunc4("sample4","callback",info);
    
    sampleFunc1("sample1","promise-pass").then (log).catch(error);
    local.sampleFunc2("sample2","promise-pass").then (log).catch(error);
    sampleFunc3("sample3","promise-pass").then (log).catch(error);
    sampleFunc4("sample4","promise-pass").then (log).catch(error);

    sampleFunc1("sample1","callback-pass",info);
    local.sampleFunc2("sample2","callback-pass",info);
    sampleFunc3("sample3","callback-pass",info);
    sampleFunc4("sample4","callback-pass",info);
    
    
    sampleFunc1("sample1","promise-fail").then (log).catch(error);
    local.sampleFunc2("sample2","promise-fail").then (log).catch(error);
    sampleFunc3("sample3","promise-fail").then (log).catch(error);
    sampleFunc4("sample4","promise-fail").then (log).catch(error);
    
    sampleFunc1("sample1","callback-fail",info);
    local.sampleFunc2("sample2","callback-fail",info);
    sampleFunc3("sample3","callback-fail",info);
    sampleFunc4("sample4","callback-fail",info);
 

var cpArgs=Array.prototype.slice.call.bind(Array.proto原型.slice);函数promiseToCB(nm,fn,THIS){如果(nm类型==='功能'){这=fn;fn=纳米;nm=fn.name;}此=此||此;const func=函数(){let args=cpArgs(参数);if(参数类型[args.length-1]=='函数'){const cb=args.pop();return fn.apply(THIS,args).then(函数(r){cb(未定义,r);}).捕获(cb);}其他{return fn.apply(THIS,args);}};Object.defineProperty(func,'name',{value:nm,enumerable:false,可配置:true});如果(此[nm])删除此[nm];Object.defineProperty(THIS,nm,{value:func,enumerable:false,可配置:true});返回函数;}函数cbToPromise(nm,fn,THIS){如果(nm类型==='功能'){这=fn;fn=纳米;nm=fn.name;}此=此||此;const func=函数(){let args=cpArgs(参数);if(参数类型[args.length-1]=='函数'){return fn.apply(THIS,args);}其他{return new Promise(函数(解析,拒绝){args.push(函数(错误,结果){if(error)返回reject(err);if(arguments.length==2){返回解析(result);}返回解析(cpArgs(arguments,1));});fn.apply(此,参数);});}};Object.defineProperty(func,'name',{value:nm,enumerable:false,可配置:true});如果(此[nm])删除此[nm];Object.defineProperty(THIS,nm,{value:func,enumerable:false,可配置:true});返回函数;}函数作为Promise(args,解析器,no_err){常量cb=参数[args.length-1],promise=新promise(解析器);return(cb类型=='函数')?promise.then(函数(结果){return cb(no-err,结果)}).catch(cb):promise;}函数作为回调(args,wrap,no_err){常量cb=参数[args.length-1],promise=新promise(函数解析器(resolve,reject){返回换行(函数(错误,结果){if(error)返回reject(err);解决(结果);});});return(cb类型=='函数')?promise.then(函数(结果){return cb(no-err,结果)}).catch(cb):promise;}函数cbPromiseTest(){/*全局样本Func1,样本Func2*/常量local={};promiseToCB(函数sampleFunc1(arg1,arg2){console.log(“决定:”,arg1,arg2);return new Promise(函数(解析,拒绝){const timer=setTimeout(函数(){reject([arg1,arg2,“ouch”].jjoin(“-”);},5000);setTimeout(函数){if(arg2.endsWith(“-pass”)||(!arg2.ends With(”-ffail“)&&Math.random()<0.5)){console.log(“完成:”,arg1,arg2);clearTimeout(计时器);解析([arg1,arg2,“一切正常”].join(“-”));}},2000);});});cbToPromise('sampleFunc2',函数someOtherName(arg1,arg2,cb){console.log(“决定:”,arg1,arg2);const timer=setTimeout(函数){cb([arg1,arg2,“ouch”].jjoin(“-”);},5000);setTimeout(函数){if(arg2.endsWith(“-pass”)||(!arg2.ends With(”-ffail“)&&Math.random()<0.5)){console.log(“完成:”,arg1,arg2);clearTimeout(计时器);cb(未定义,[arg1,arg2,“all good”]。join(“-”));}},2000);},本地);函数sampleFunc3(arg1,arg2){return asPromise(参数,函数(解析,拒绝){console.log(“决定:”,arg1,arg2);const timer=setTimeout(函数(){reject([arg1,arg2,“ouch”].jjoin(“-”);},5000);setTimeout(函数){if(arg2.endsWith(“-pass”)||(!arg2.ends With(”-ffail“)&&Math.random()<0.5)){console.log(“完成:”,arg1,arg2);clearTimeout(计时器);解析([arg1,arg2,“一切正常”].join(“-”));}},2000);});}函数sampleFunc4(arg1,arg2){return asCallback(参数,函数(cb){console.log(“决定:”,arg1,arg2);const timer=setTimeout(函数){cb([arg1,arg2,“ouch”].jjoin(“-”);},5000);setTimeout(函数){if(arg2.endsWith(“-pass”)||(!arg2.ends With(”-ffail“)&&Math.random()<0.5)){console.log(“完成:”,arg1,arg2);clearTimeout(计时器);cb(未定义,[arg1,arg2,“all good”]。join(“-”));}},2000);});}const log=console.log.bind(控制台),info=console.info.bind(控制台,error=console.error.bind);sampleFunc1(“sample1”,“promise”).then(log).catch(error);local.sampleFunc2(“sample2”,“promise”).then(log).catch(error);sampleFunc3(“sample3”,“promise”).then(log).catch(error);sampleFunc4(“sample4”,“promise”).then(log).catch(error);sampleFunc1(“sample1”,“回调”,信息);local.sampleFunc2(“sample2”,“回调”,信息);sampleFunc3(“sample3”,“回调”,信息);sampleFunc4(“sample4”,“回调”,信息);sampleFunc1(“sample1”,“promise pass”).then(log).catch(error);local.sampleFunc2(“sample2”,“pr”

在Node.js 8.0.0的候选版本中,有一个新的实用程序util.profisify(我已经写过util.proficify),它封装了promising任何函数的能力。

它与其他答案中建议的方法没有太大不同,但具有作为核心方法而不需要额外依赖性的优点。

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返回函数抛出将迫使您同时使用一个}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。

您可以在NodeJS中使用JavaScript本机承诺。

My Cloud 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