P粉6183582602023-08-22 16:14:21
今天,我可以在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
一起使用,使程式流程等待fulfilled
結果,如下所示:
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()方法运行代码
使用相同的程式碼,可以使用.then()
方法:
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
也可以在任何基於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 ) }) }
上述方法可以同時回應舊式的回呼和Promise用法的結果。
希望這能幫到你。
P粉6800875502023-08-22 11:07:09
承諾具有狀態,它們開始時處於掛起狀態,可以解決為:
傳回承諾的函數不應該拋出例外,而應該回傳拒絕。從傳回承諾的函數中拋出例外狀況將強制您同時使用} catch {
和 .catch
。使用承諾化的API的人們不希望承諾拋出異常。如果您不確定JS中的非同步API如何運作,請先查看此答案。
因此,創建承諾通常意味著指定它們何時解決-這意味著它們何時移動到已實現或已拒絕階段以指示資料可用(並可使用.then
存取)。
使用支援Promise
建構子的現代承諾實作(如原生ES6承諾):
function load() { return new Promise(function(resolve, reject) { window.onload = resolve; }); }
然後您可以這樣使用生成的承諾:
load().then(function() { // 在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(); }
這些API相當常見,因為在JS中回呼很常見。讓我們來看看常見情況下的onSuccess
和onFail
:
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延遲的resolve
和reject
方法是「可分離」的事實;即它們綁定到jQuery.Deferred()的實例。並非所有庫都提供此功能。
Node風格回呼(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現在支援新語法您應該優先選擇該語法):
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 中的原生承諾都具有用於將nodebacks轉換為承諾的內建方法。例如
var getStuffAsync = Promise.promisify(getStuff); // Bluebird var getStuffAsync = Q.denodeify(getStuff); // Q var getStuffAsync = util.promisify(getStuff); // 原生承诺,仅限Node
這裡沒有黃金法則,您可以逐一將它們轉換為承諾。但是,某些承諾實作允許您批量執行此操作,例如在Bluebird中,將nodeback API轉換為承諾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
處理程序傳回一個承諾將使用該承諾的值解決或拒絕。從.then
處理程序中拋出異常也是良好的實踐,將拒絕該承諾-這就是著名的承諾拋出安全性。 onload
情況中,您應該使用addEventListener
而不是onX
。