如何将现有的回调API转换为承诺?

时间:2014-03-19 22:47:26

标签: javascript node.js callback promise bluebird

我想使用promises,但我有一个回调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) {
            ...
        });
    });
});

我如何在承诺中使用API​​,我如何" promisify"它?

22 个答案:

答案 0 :(得分:659)

承诺有状态,他们开始等待并且可以安顿下来:

  • 履行意味着计算成功完成。
  • 拒绝表示计算失败。

承诺返回函数should never throw,他们应该返回拒绝。从承诺返回功能开始将强制您同时使用} catch { a .catch。使用promisified API的人不希望承诺投掷。如果您不确定异步API如何在JS中运行 - 请先see this answer

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

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

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

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

然后您将使用生成的承诺:

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

使用支持延迟的库(让我们在这个例子中使用$ 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中很常见。让我们来看看onSuccessonFail

的常见情况
function getUserData(userId, onLoad, onFail) { …

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

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)表单,其优点是允许我们编写一个非常接近new Promise(fn)表单的表达式,如下所示:

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

注意:在这里我们利用jQuery延迟的resolvereject方法可以解决这个问题&#34 ;;即。它们绑定到jQuery.Deferred()的实例。并非所有的lib都提供此功能。

3。节点样式回调(" nodeback"):

节点样式回调(nodebacks)具有特定格式,其中回调始终是最后一个参数,其第一个参数是错误。让我们首先手动宣传:

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

要:

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

使用延迟,您可以执行以下操作(在此示例中使用Q,尽管Q现在支持新语法which you should prefer):

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

一般情况下,你不应该手动宣传过多的东西,大多数承诺在设计时考虑到Node以及Node 8+中的本机承诺的库都有内置的方法来实现节点回送。例如

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

节点中的原生承诺

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

注意:

  • 当然,当你在.then处理程序时,你不需要宣传事物。从.then处理程序返回承诺将解决或拒绝该承诺的价值。从.then处理程序投掷也是很好的做法,并且会拒绝承诺 - 这是着名的承诺投掷安全。
  • 在实际的onload案例中,您应该使用addEventListener而不是onX

答案 1 :(得分:46)

今天,我可以在Promise中使用Node.js作为简单的Javascript方法。

Promise的简单基本示例(使用 KISS 方式):

简单 Javascript异步API代码:

function divisionAPI (number, divider, successCallback, errorCallback) {

    if (divider == 0) {
        return errorCallback( new Error("Division by zero") )
    }

    successCallback( number / divider )

}

Promise Javascript异步API代码:

function divisionAPI (number, divider) {

    return new Promise(function (fulfilled, rejected) {

        if (divider == 0) {
            return rejected( new Error("Division by zero") )
        }

        fulfilled( number / divider )

     })

}

(我建议访问this beautiful source

同样Promise可与async\await中的ES7一起使用,以使程序流等待fullfiled结果,如下所示:

function getName () {

    return new Promise(function (fulfilled, rejected) {

        var name = "John Doe";

        // wait 3000 milliseconds before calling fulfilled() method
        setTimeout ( 
            function() {
                fulfilled( name )
            }, 
            3000
        )

    })

}


async function foo () {

    var name = await getName(); // awaits for a fulfilled result!

    console.log(name); // the console writes "John Doe" after 3000 milliseconds

}


foo() // calling the foo() method to run the code

使用.then()方法

使用相同代码的另一种用法
function getName () {

    return new Promise(function (fulfilled, rejected) {

        var name = "John Doe";

        // wait 3000 milliseconds before calling fulfilled() method
        setTimeout ( 
            function() {
                fulfilled( name )
            }, 
            3000
        )

    })

}


// the console writes "John Doe" after 3000 milliseconds
getName().then(function(name){ console.log(name) })

Promise也可用于任何基于Node {js的平台,例如react-native

奖金混合方法
(假设回调方法有两个参数作为错误和结果)

function divisionAPI (number, divider, callback) {

    return new Promise(function (fulfilled, rejected) {

        if (divider == 0) {
            let error = new Error("Division by zero")
            callback && callback( error )
            return rejected( error )
        }

        let result = number / divider
        callback && callback( null, result )
        fulfilled( result )

     })

}

上述方法可以响应旧时尚回调和承诺使用的结果。

希望这有帮助。

答案 2 :(得分:26)

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

答案 3 :(得分:21)

我认为@Benjamin的window.onload建议不会一直有效,因为它不会检测是否在加载后调用它。我被这多次咬过了。这是一个应该始终有效的版本:

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

答案 4 :(得分:12)

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

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

const fs = require('fs');
const util = require('util');

const readFile = util.promisify(fs.readFile);

然后你有一个返回原生readFile的{​​{1}}方法。

Promise

答案 5 :(得分:8)

Node.js 8.0.0包含一个新的util.promisify() API,允许将标准Node.js回调样式API包装在返回Promise的函数中。 util.promisify()的示例用法如下所示。

const fs = require('fs');
const util = require('util');

const readFile = util.promisify(fs.readFile);

readFile('/some/file')
  .then((data) => { /** ... **/ })
  .catch((err) => { /** ... **/ });

请参阅Improved support for Promises

答案 6 :(得分:5)

您可以在Node JS中使用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

答案 7 :(得分:4)

kriskowal的Q库包括回调承诺函数。 像这样的方法:

obj.prototype.dosomething(params, cb) {
  ...blah blah...
  cb(error, results);
}

可以使用Q.ninvoke转换

Q.ninvoke(obj,"dosomething",params).
then(function(results) {
});

答案 8 :(得分:3)

在节点v7.6 +内置了promises和async:

// promisify.js
let promisify = fn => (...args) =>
    new Promise((resolve, reject) =>
        fn(...args, (err, result) => {
            if (err) return reject(err);
            return resolve(result);
        })
    );

module.exports = promisify;

使用方法:

let readdir = require('fs').readdir;
let promisify = require('./promisify');
let readdirP = promisify(readdir);

async function myAsyncFn(path) {
    let entries = await readdirP(path);
    return entries;
}

答案 9 :(得分:2)

如果你有一些函数需要回调并且你希望它们返回一个promise,你可以使用这个函数来进行转换。

function callbackToPromise(func){

    return function(){

        // change this to use what ever promise lib you are using
        // In this case i'm using angular $q that I exposed on a util module

        var defered = util.$q.defer();

        var cb = (val) => {
            defered.resolve(val);
        }

        var args = Array.prototype.slice.call(arguments);
        args.push(cb);    
        func.apply(this, args);

        return defered.promise;
    }
}

答案 10 :(得分:2)

使用简单的旧版香草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);
});

答案 11 :(得分:2)

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

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

它使用 util.promisify 代理,以便您的对象保持不变。 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);
    // ...
  });

您甚至可以使用原生callapply来绑定某些上下文:

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

答案 12 :(得分:1)

回调样式函数总是这样(node.js中的几乎所有函数都是这种样式):

//fs.readdir(path[, options], callback)
fs.readdir('mypath',(err,files)=>console.log(files))

此样式具有相同的功能:

  1. 回调函数由最后一个参数传递。

  2. 回调函数总是接受错误对象作为它的第一个参数。

  3. 所以,你可以用这样的方式编写一个转换函数的函数:

    const R =require('ramda')
    
    /**
     * A convenient function for handle error in callback function.
     * Accept two function res(resolve) and rej(reject) ,
     * return a wrap function that accept a list arguments,
     * the first argument as error, if error is null,
     * the res function will call,else the rej function.
     * @param {function} res the function which will call when no error throw
     * @param {function} rej the function which will call when  error occur
     * @return {function} return a function that accept a list arguments,
     * the first argument as error, if error is null, the res function
     * will call,else the rej function
     **/
    const checkErr = (res, rej) => (err, ...data) => R.ifElse(
        R.propEq('err', null),
        R.compose(
            res,
            R.prop('data')
        ),
        R.compose(
            rej,
            R.prop('err')
        )
    )({err, data})
    
    /**
     * wrap the callback style function to Promise style function,
     * the callback style function must restrict by convention:
     * 1. the function must put the callback function where the last of arguments,
     * such as (arg1,arg2,arg3,arg...,callback)
     * 2. the callback function must call as callback(err,arg1,arg2,arg...)
     * @param {function} fun the callback style function to transform
     * @return {function} return the new function that will return a Promise,
     * while the origin function throw a error, the Promise will be Promise.reject(error),
     * while the origin function work fine, the Promise will be Promise.resolve(args: array),
     * the args is which callback function accept
     * */
     const toPromise = (fun) => (...args) => new Promise(
        (res, rej) => R.apply(
            fun,
            R.append(
                checkErr(res, rej),
                args
            )
        )
    )
    

    为了更简洁,上面的例子使用了ramda.js。 Ramda.js是一个优秀的函数式编程库。在上面的代码中,我们使用了 apply (如javascript function.prototype.apply)并追加(如javascript function.prototype.push)。 因此,我们现在可以将回调样式函数转换为promise样式函数:

    const {readdir} = require('fs')
    const readdirP = toPromise(readdir)
    readdir(Path)
        .then(
            (files) => console.log(files),
            (err) => console.log(err)
        )
    

    toPromise checkErr 函数由berserk库拥有,它是一个功能性编程库,由 ramda.js (由我创造)。

    希望这个答案对你有用。

答案 13 :(得分:1)

从未来开始

我通常使用的简单通用函数。

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

使用方法

   promisify(fn, arg1, arg2)

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

答案 14 :(得分:1)

您可以这样做

// @flow

const toPromise = (f: (any) => void) => {
  return new Promise<any>((resolve, reject) => {
    try {
      f((result) => {
        resolve(result)
      })
    } catch (e) {
      reject(e)
    }
  })
}

export default toPromise

然后使用它

async loadData() {
  const friends = await toPromise(FriendsManager.loadFriends)

  console.log(friends)
}

答案 15 :(得分:1)

您可以在ES6中使用原生承诺,例如处理setTimeout:

enqueue(data) {

    const queue = this;
    // returns the Promise
    return new Promise(function (resolve, reject) {
        setTimeout(()=> {
                queue.source.push(data);
                resolve(queue); //call native resolve when finish
            }
            , 10); // resolve() will be called in 10 ms
    });

}

在这个例子中,Promise没有理由失败,因此永远不会调用reject()

答案 16 :(得分:0)

callback函数的promisify版本是P函数:

var P = function() {
  var self = this;
  var method = arguments[0];
  var params = Array.prototype.slice.call(arguments, 1);
  return new Promise((resolve, reject) => {
    if (method && typeof(method) == 'function') {
      params.push(function(err, state) {
        if (!err) return resolve(state)
        else return reject(err);
      });
      method.apply(self, params);
    } else return reject(new Error('not a function'));
  });
}
var callback = function(par, callback) {
  var rnd = Math.floor(Math.random() * 2) + 1;
  return rnd > 1 ? callback(null, par) : callback(new Error("trap"));
}

callback("callback", (err, state) => err ? console.error(err) : console.log(state))
callback("callback", (err, state) => err ? console.error(err) : console.log(state))
callback("callback", (err, state) => err ? console.error(err) : console.log(state))
callback("callback", (err, state) => err ? console.error(err) : console.log(state))

P(callback, "promise").then(v => console.log(v)).catch(e => console.error(e))
P(callback, "promise").then(v => console.log(v)).catch(e => console.error(e))
P(callback, "promise").then(v => console.log(v)).catch(e => console.error(e))
P(callback, "promise").then(v => console.log(v)).catch(e => console.error(e))

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

答案 17 :(得分:0)

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

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

const promisedFn = promisify(callbackedFn, args);

参考:https://www.npmjs.com/package/es6-promisify

答案 18 :(得分:0)

以下是如何将函数(回调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');

答案 19 :(得分:0)

死灵法术一点,这个link可能有用....


TLDR;查看此答案末尾的代码段示例


可以调用的写入/转换函数

cb(error,result)new Promise (...) 格式


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

示例函数

每个样本接受 2 个参数,并根据随机数解决/拒绝/错误。

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

包装现有函数

  • 将函数导出到当前的“this”(或使用 promiseToCB(function 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);
        
    });}

以上功能的测试脚本


    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.prototype.slice);

    function promiseToCB (nm,fn,THIS) {
        if (typeof nm==='function') {
            THIS=fn;fn=nm;nm=fn.name;
        }
        THIS=THIS||this;
        const func = function () {
           let args = cpArgs(arguments);
            if (typeof args[args.length-1]==='function') {
                const cb = args.pop();
                return fn.apply(THIS,args).then(function(r){
                   cb (undefined,r);
                }).catch(cb);  
            } else {
                return fn.apply(THIS,args);
            }
        };
        Object.defineProperty(func,'name',{value:nm,enumerable:false,configurable: true});
        if (THIS[nm]) delete THIS[nm];
        Object.defineProperty(THIS,nm,{value:func,enumerable:false,configurable: true});
        return func;
    }

    function cbToPromise (nm,fn,THIS) {
        if (typeof nm==='function') {
            THIS=fn;fn=nm;nm=fn.name;
        }
        THIS=THIS||this;
        const func = function () {
           let args = cpArgs(arguments);
            if (typeof args[args.length-1]==='function') {
                return fn.apply(THIS,args);
            } else {
                return new Promise(function(resolve,reject){
                    
                    args.push(function(err,result){
                          if (err) return reject(err);
                          if (arguments.length==2) {
                             return resolve(result);
                          }
                          return resolve(cpArgs(arguments,1));
                    });
                              
                    fn.apply(THIS,args);
                    
                });
            }
        };
        Object.defineProperty(func,'name',{value:nm,enumerable:false,configurable: true});
        if (THIS[nm]) delete THIS[nm];
        Object.defineProperty(THIS,nm,{value:func,enumerable:false,configurable: true});
        return func;

    }

    function asPromise (args,resolver,no_err) {
        const cb = args[args.length-1],
        promise  = new Promise(resolver);
        return (typeof cb==='function')  ? promise.then(function(result){return cb(no_err,result)}).catch(cb) : promise;
    }

    function asCallback (args,wrap,no_err) {
        const cb = args[args.length-1],
        promise=new Promise(function resolver(resolve,reject) {
            return wrap (function (err,result) {
                 if (err) return reject(err);
                 resolve(result);
            });
        });
        return (typeof cb==='function')  ? promise.then(function(result){return cb(no_err,result)}).catch(cb) : promise;
    }


    function cbPromiseTest(){
        /*global sampleFunc1,sampleFunc2*/
        
        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);
     
    }
    cbPromiseTest();

答案 20 :(得分:-1)

我已经创建了可以重用的通用函数。

       const wrapIntoPromise: (fnToExecute) => {
            return new Promise((resolve, reject) => {
                return fnToExecute(resolve, reject);
            });
        }

        //example
        async function run() {
           await wrapIntoPromise((resolve, reject) => { 
               /*execute any function here with callbacks etc
                when done just call return resolve(valueToReturn)
               */ 
               return backup({
                  callback: function() { return resolve()}
               });

           });
        }

答案 21 :(得分:-2)

大概晚了5年,但我想在这里发布我的Promesify版本,该版本采用回调API的功能并将其转化为Promise

const promesify = fn => {
  return (...params) => ({
    then: cbThen => ({
      catch: cbCatch => {
        fn(...params, cbThen, cbCatch);
      }
    })
  });
};

在这里看看这个非常简单的版本: https://gist.github.com/jdtorregrosas/aeee96dd07558a5d18db1ff02f31e21a