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

本文翻译自:How do I convert an existing callback API to promises?

I want to work with promises but I have a callback API in a format like: 我想使用Promise,但是我有一个类似以下格式的回调API:

1. DOM load or other one time event: 1. DOM加载或其他一次事件:

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

};

2. Plain callback: 2.普通回调:

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

3. Node style callback ("nodeback"): 3.节点样式回调(“ nodeback”):

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

4. A whole library with node style callbacks: 4.带有节点样式回调的整个库:

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

How do I work with the API in promises, how do I "promisify" it? 如何在promise中使用API​​,如何“承诺”它?


#1楼

参考:https://stackoom.com/question/1WUQK/如何将现有的回调API转换为Promise


#2楼

Promises have state, they start as pending and can settle to: 承诺有状态,它们从待定状态开始,可以解决:

  • fulfilled meaning that the computation completed successfully. 完成意味着计算成功完成。
  • rejected meaning that the computation failed. 拒绝表示计算失败。

Promise returning functions should never throw , they should return rejections instead. 承诺返回函数绝不应该抛出 ,而应该返回拒绝。 Throwing from a promise returning function will force you to use both a } catch { and a .catch . 从promise返回函数抛出将迫使您同时使用} catch { .catch People using promisified APIs do not expect promises to throw. 使用承诺的API的人们不会期望诺言。 If you're not sure how async APIs work in JS - please see this answer first. 如果您不确定JS中异步API的工作方式-请首先查看此答案

1. DOM load or other one time event: 1. DOM加载或其他一次事件:

So, creating promises generally means specifying when they settle - that means when they move to the fulfilled or rejected phase to indicate the data is available (and can be accessed with .then ). 因此,创建承诺通常意味着指定何时结算-即何时进入承诺阶段或拒绝阶段以指示数据可用(并且可以通过.then访问)。

With modern promise implementations that support the Promise constructor like native ES6 promises: 使用支持Promise构造函数的现代Promise实现(例如本机ES6 Promise

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

You would then use the resulting promise like so: 然后,您将使用产生的承诺,如下所示:

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

With libraries that support deferred (Let's use $q for this example here, but we'll also use jQuery later): 使用支持延迟的库(让我们在此示例中使用$ q,但稍后我们还将使用jQuery):

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

Or with a jQuery like API, hooking on an event happening once: 或者使用jQuery之类的jQuery,将一次事件挂起:

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

2. Plain callback: 2.普通回调:

These APIs are rather common since well… callbacks are common in JS. 这些API相当常见,因为……在JS中回调很常见。 Let's look at the common case of having onSuccess and onFail : 让我们看一下具有onSuccessonFail的常见情况:

function getUserData(userId, onLoad, onFail) { …

With modern promise implementations that support the Promise constructor like native ES6 promises: 使用支持Promise构造函数的现代Promise实现(例如本机ES6 Promise

function getUserDataAsync(userId) {
    return new Promise(function(resolve, reject) {
        getUserData(userId, resolve, reject);
    });
}

With libraries that support deferred (Let's use jQuery for this example here, but we've also used $q above): 使用支持延迟的库(在此示例中,我们使用jQuery,但上面我们也使用$ q):

function getUserDataAsync(userId) {
    var d = $.Deferred();
    getUserData(userId, function(res){ d.resolve(res); }, function(err){ d.reject(err); });
    return d.promise();
}

jQuery also offers a $.Deferred(fn) form, which has the advantage of allowing us to write an expression that emulates very closely the new Promise(fn) form, as follows: jQuery还提供了$.Deferred(fn)形式,它的优点是允许我们编写一个非常接近new Promise(fn)形式的表达式,如下所示:

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

Note: Here we exploit the fact that a jQuery deferred's resolve and reject methods are "detachable"; 注意:这里我们利用了一个事实,即jQuery deferred的resolvereject方法是“可分离的”。 ie. 即。 they are bound to the instance of a jQuery.Deferred(). 它们绑定到jQuery.Deferred()的实例 Not all libs offer this feature. 并非所有库都提供此功能。

3. Node style callback ("nodeback"): 3.节点样式回调(“ nodeback”):

Node style callbacks (nodebacks) have a particular format where the callbacks is always the last argument and its first parameter is an error. 节点样式回调(nodebacks)具有特定的格式,其中回调始终是最后一个参数,而其第一个参数是错误。 Let's first promisify one manually: 让我们首先手动分配一个:

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

With deferreds you can do the following (let's use Q for this example, although Q now supports the new syntax which you should prefer ): 使用deferred,您可以执行以下操作(尽管本例中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;   
}

In general, you should not promisify things manually too much, most promise libraries that were designed with Node in mind as well as native promises in Node 8+ have a built in method for promisifying nodebacks. 通常,您不应该过多地手动分配内容,大多数基于Node设计的Promise库以及Node 8+中的本机Promise具有内置的用于使NodeBback富集的方法。 For example 例如

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

4. A whole library with node style callbacks: 4.带有节点样式回调的整个库:

There is no golden rule here, you promisify them one by one. 这里没有黄金法则,您一一承诺。 However, some promise implementations allow you to do this in bulk, for example in Bluebird, converting a nodeback API to a promise API is as simple as: 但是,某些promise实现允许您批量执行此操作,例如在Bluebird中,将nodeback API转换为promise API很简单:

Promise.promisifyAll(API);

Or with native promises in Node : 或在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}), {});

Notes: 笔记:

  • Of course, when you are in a .then handler you do not need to promisify things. 当然,当你在一个.then处理你不需要promisify事情。 Returning a promise from a .then handler will resolve or reject with that promise's value. .then处理程序返回一个诺言将解决或拒绝该诺言的值。 Throwing from a .then handler is also good practice and will reject the promise - this is the famous promise throw safety. .then处理程序中抛出也是很好的做法,并且会拒绝诺言-这就是著名的诺言抛出安全性。
  • In an actual onload case, you should use addEventListener rather than onX . 在实际的onload情况下,应该使用addEventListener而不是onX

#3楼

I don't think the window.onload suggestion by @Benjamin will work all the time, as it doesn't detect whether it is called after the load. 我认为@Benjamin的window.onload建议不会一直有效,因为它无法检测到加载后是否被调用。 I have been bitten by that many times. 我被很多次咬伤了。 Here is a version which should always work: 这是一个应该始终有效的版本:

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

#4楼

The Q library by kriskowal includes callback-to-promise functions. kriskowal的Q库包含应答回调函数。 A method like this: 这样的方法:

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

can be converted with Q.ninvoke 可以用Q.ninvoke转换

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

#5楼

You can use JavaScript native promises with Node JS. 您可以将JavaScript本机Promise与Node JS一起使用。

My Cloud 9 code link: https://ide.c9.io/adx2803/native-promises-in-node 我的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

#6楼

When you have a few functions that take a callback and you want them to return a promise instead you can use this function to do the conversion. 当您有一些需要回调的函数并且希望它们返回一个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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值