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


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

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

承诺返回函数不应该抛出,而是应该返回拒绝。从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);

kriskowal的Q库包含对promise函数的回调。这样的方法:

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

可以使用Q.invoke进行转换

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

您可以在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

如果有几个函数需要回调,并且希望它们返回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;
    }
}

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

今天,我可以在Node.js中使用Promise作为简单的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 )

     })

}

(我建议访问这个美丽的来源)

此外,Promise可以与ES7中的async\await一起使用,以使程序流等待完整的结果,如下所示:

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。

奖金:混合方法(假设回调方法有两个参数,即error和result)

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 )

     })

}

上面的方法可以响应老式回调和Promise用法的结果。

希望这有帮助。


您可以在ES6中使用本机Promise,例如处理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()。


在内置promise和异步的节点v7.6+下:

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

在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(...);

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

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

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

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

请参阅改进对承诺的支持


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

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

此样式具有相同的功能:

回调函数由最后一个参数传递。回调函数始终接受错误对象作为其第一个参数。

因此,您可以编写一个函数来转换具有以下样式的函数:

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)和append(如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(由我创建)的函数式编程库fork。

希望这个答案对你有用。


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

在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 => { /*...*/ });

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

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

const promisedFn = promisify(callbackedFn, args);

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


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


你可以这样做

// @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)
}

这已经晚了5年,但我想在这里发布我的promisify版本,它从回调API中获取函数,并将它们转化为承诺

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

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


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


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

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

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


也许已经回答了,但我通常是这样做的:

// given you've defined this `Future` fn somewhere:
const Future = fn => {return new Promise((r,t) => fn(r,t))}

// define an eventFn that takes a promise `resolver`
const eventFn = resolve => {
  // do event related closure actions here. When finally done, call `resolve()`
  something.oneventfired = e => {resolve(e)}
}

// invoke eventFn in an `async` workflowFn using `Future`
// to obtain a `promise` wrapper
const workflowFn = async () => {await Future(eventFn)}

特别是对于indexedDb事件包装器之类的东西,以简化使用。

或者你可能会发现未来的这种变化更具普遍性

class PromiseEx extends Promise {
  resolve(v,...a) {
    this.settled = true; this.settledValue = v;
    return(this.resolve_(v,...a))
  }
  reject(v,...a) {
    this.settled = false; this.settledValue = v;
    return(this.reject_(v,...a))
  }
  static Future(fn,...args) {
    let r,t,ft = new PromiseEx((r_,t_) => {r=r_;t=t_})
    ft.resolve_ = r; ft.reject_ = t; fn(ft,...args);
    return(ft)
  }
}

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


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


编写/转换可调用的函数

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”


承诺总是有决心和拒绝。当您编写异步包装器时,只需调用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上寻找一个现代的包装器,或者像这样编写自己的包装器。


由于我们预先知道基于回调的函数的特性,我们可以创建一个函数来转换基于回调的函数转换为返回Promise的等效函数。

回调是函数的最后一个参数如果有错误,它总是传递给回调的第一个参数错误发生后,任何返回值都会传递给回调函数promisify(yourCallbackApi){返回函数promised(…args){return new Promise((resolve,reject)=>{//newArgs=[..args,回调]常量newArgs=[…参数,函数(错误,结果){if(错误){返回拒绝(错误);}解决(结果);},];//使用新的参数列表调用CallbackApi您的回调Api(…newArgs);});};}