P粉6183582602023-08-22 16:14:21
Today, I can use Promise
as a normal Javascript method in Node.js
.
A simple and basic Promise
example (using the KISS method):
NormalJavascript asynchronous API code:
function divisionAPI (number, divider, successCallback, errorCallback) { if (divider == 0) { return errorCallback( new Error("Division by zero") ) } successCallback( number / divider ) }
Promise
Javascript asynchronous API code:
function divisionAPI (number, divider) { return new Promise(function (fulfilled, rejected) { if (divider == 0) { return rejected( new Error("Division by zero") ) } fulfilled( number / divider ) }) }
(I recommend visiting this excellent source )
In addition, Promise
can also be used with async\await
in ES7
to make the program flow wait for the fulfilled
result, as follows Shown:
function getName () { return new Promise(function (fulfilled, rejected) { var name = "John Doe"; // 在调用fulfilled()方法之前等待3000毫秒 setTimeout ( function() { fulfilled( name ) }, 3000 ) }) } async function foo () { var name = await getName(); // 等待fulfilled结果! console.log(name); // 控制台在3000毫秒后输出"John Doe" } foo() // 调用foo()方法运行代码
Using the same code, you can use the .then()
method:
function getName () { return new Promise(function (fulfilled, rejected) { var name = "John Doe"; // 在调用fulfilled()方法之前等待3000毫秒 setTimeout ( function() { fulfilled( name ) }, 3000 ) }) } // 控制台在3000毫秒后输出"John Doe" getName().then(function(name){ console.log(name) })
Promise
can also be used on any Node.js-based platform, such as react-native
.
Bonus: A hybridapproach
(Assume that the callback method has two parameters, namely error and 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 ) }) }
The above method can respond to the results of old-style callbacks and Promise usage at the same time.
Hope this helps.
P粉6800875502023-08-22 11:07:09
Promises have a state, they start out in a pending state and can be resolved as:
Functions that return a promise should not throw an exception but should return a rejection. Throwing an exception from a function that returns a promise will force you to use both } catch {
and .catch
. People using promise-based APIs don't want promises to throw exceptions. If you're not sure how the async API in JS works, first check out this answer.
Therefore, creating promises usually means specifying when they resolve - meaning when they move to the Fulfilled or Rejected stage to indicate that the data is available (and accessible using .then
).
Use a modern promise implementation that supports Promise
constructors (such as native ES6 promises):
function load() { return new Promise(function(resolve, reject) { window.onload = resolve; }); }
You can then use the generated promise like this:
load().then(function() { // 在onload之后执行操作 });
Use a library that supports delay (here we use $q as an example, but we will also use jQuery later):
function load() { var d = $q.defer(); window.onload = function() { d.resolve(); }; return d.promise; }
Or use an API similar to jQuery to hook an event that occurs:
function done() { var d = $.Deferred(); $("#myObject").once("click",function() { d.resolve(); }); return d.promise(); }
These APIs are quite common because callbacks are common in JS. Let's look at onSuccess
and onFail
in common cases:
function getUserData(userId, onLoad, onFail) { …
Use a modern promise implementation that supports Promise
constructors (such as native ES6 promises):
function getUserDataAsync(userId) { return new Promise(function(resolve, reject) { getUserData(userId, resolve, reject); }); }
Use a library that supports delay (here we're using jQuery as an example, but we also used $q before):
function getUserDataAsync(userId) { var d = $.Deferred(); getUserData(userId, function(res){ d.resolve(res); }, function(err){ d.reject(err); }); return d.promise(); }
jQuery also provides the $.Deferred(fn)
form, which has the advantage of allowing us to write an expression very close to the new Promise(fn)
form, as shown below :
function getUserDataAsync(userId) { return $.Deferred(function(dfrd) { getUserData(userId, dfrd.resolve, dfrd.reject); }).promise(); }
Note: Here we take advantage of the fact that jQuery's deferred resolve
and reject
methods are "detachable"; that is, they are bound to jQuery.Deferred()'s Example. Not all libraries provide this functionality.
Node style callbacks (nodebacks) have a specific format where the callback is always the last parameter and its first parameter is the error. First manually convert it to a promise:
getStuff("dataParam", function(err, data) { …
Convert to:
function getStuffAsync(param) { return new Promise(function(resolve, reject) { getStuff(param, function(err, data) { if (err !== null) reject(err); else resolve(data); }); }); }
Using defer, you can do the following (we used Q as an example, although Q now supports a new syntax You should prefer that syntax ):
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 shouldn't manually convert things to promises too much, most promise libraries designed for Node as well as native promises in Node 8 have built-in methods for converting nodebacks to promises. For example
var getStuffAsync = Promise.promisify(getStuff); // Bluebird var getStuffAsync = Q.denodeify(getStuff); // Q var getStuffAsync = util.promisify(getStuff); // 原生承诺,仅限Node
There is no golden rule here, you can convert them into promises one by one. However, some promise implementations allow you to do this in batches, for example in Bluebird converting the nodeback API to a promise API is as simple as this:
Promise.promisifyAll(API);
Or use Native Promise in 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}), {});
Notice:
.then
handler. Returning a promise from the .then
handler will be resolved or rejected using the value of the promise. It is also good practice to throw an exception from the .then
handler, which will reject the promise - this is known as promise-throwing safety. onload
case, you should use addEventListener
instead of onX
.