今回はノードでコールバック関数の代わりにPromiseを使う方法を紹介します。ノードでコールバック関数の代わりにPromiseを使うときの注意点を実際に見てみましょう。
async の本質は まず、考えてみましょう 典型的な非同期プログラミング モデルについて考えてみましょう: ファイルを読み取り、ファイルの内容をコンソールに出力しますvar fs = require('fs'); fs.readFile('1.txt', 'utf8', function (err, data) { console.log(data); });は非常に簡単に思えますが、さらに一歩進めてください: 2 つのファイルを読み取り、2 つのファイルの内容をコンソールに出力しますコンソール
var fs = require('fs'); fs.readFile('1.txt', 'utf8', function (err, data) { console.log(data); fs.readFile('2.txt', 'utf8', function (err, data) { console.log(data); }); });さらにファイルを読み込んだらどうなるか
var fs = require('fs'); fs.readFile('1.txt', 'utf8', function (err, data) { fs.readFile('2.txt', 'utf8', function (err, data) { fs.readFile('3.txt', 'utf8', function (err, data) { fs.readFile('4.txt', 'utf8', function (err, data) { // ... }); }); }); });これは伝説的なコールバック地獄です。このコードを改善するには async を使用できますが、この場合は、promise/defer を使用して改善する必要があります まず、通常のJavaScriptのオブジェクトと何ら変わらないオブジェクトであると同時に、最終的な結果を表す非同期操作の統一インターフェースに準拠した仕様でもあります。同期する非同期操作 操作を非同期に実行する方法でコードを記述することですが、プログラムの実行順序が同期であることを保証します1. Promise の状態は、未完了、履行、拒否の 3 つだけです2.不完全から完了、または不完全から失敗に変換されます3. Promise の状態遷移は 1 回だけ発生しますPromise には then メソッドがあり、then メソッドは 3 つの関数をパラメータとして受け取ることができます。最初の 2 つの関数は、約束の 2 つの状態 (履行と拒否) のコールバック関数に対応します。 3 番目の関数は進行状況情報を処理するために使用されます それを理解するには、いくつかの重要な原則を覚えておく必要があります: .then() は常に次のコードのような新しい Promise を返します:
var promise = readFile() var promise2 = promise.then(readAnotherFile, console.error)ここで then のパラメータは readAnotherFile ですconsole.error 非同期操作が成功した後のアクション onFulfilled または失敗後のアクション OnRejected を表します。つまり、ファイルが正常に読み取られた後に readAnotherFile 関数が実行されます。それ以外の場合は、失敗時にエラーが出力および記録されます。この実装は 2 つの可能性のうちの 1 つにすぎません は次のように理解することもできます:
promiseSomething().then(function (fulfilled) { // 当 promise 状态变成 fulfilled 时,调用此函数 }, function (rejected) { // 当 promise 状态变成 rejected 时,调用此函数 }, function (progress) { // 当返回进度信息时,调用此函数 });Promise の法則には分離する必要がある 2 つの部分があります: 1 then() は呼び出すたびに常に新しい Promise を返します。 , コールバックが呼び出される前に .then() によって Promise が与えられるため、コールバックの動作は関係ありません。コールバックが値を返す場合、Promise はその実装にのみ影響します。値がこの値にこの Promise の実装された値を返す Promise の場合、Promise はエラーを拒否します2。新しい Promise は、呼び出される Promise の .then( ) とは異なります。長い Promise チェーンでは、.then() を呼び出すたびに新しい Promise が生成されるという事実が隠蔽されることがあります。ここで、実際に必要なものに注意する必要があります。考慮すべきは、最後に行った呼び出しです。 then() は失敗を表す可能性があるため、この失敗を検出しないと、エラー例外が簡単に消えてしまいます この問題を処理するために q を使用する簡単な例を見てみましょう。 :
var Q = require('q'); var defer = Q.defer(); /** * 获取初始 promise * @private */ function getInitialPromise() { return defer.promise; } /** * 为 promise 设置三种状态的回调函数 */ getInitialPromise().then(function (success) { console.log(success); }, function (error) { console.log(error); }, function (progress) { console.log(progress); }); defer.notify('in progress'); // 控制台打印 in progress defer.resolve('resolve'); // 控制台打印 resolve defer.reject('reject'); // 没有输出。promise 的状态只能改变一次
Promiseの受け渡し
ThenメソッドはPromiseを返します。 次の例では、outputPromiseを使用してthenによって返されたPromiseを指します。var outputPromise = getInputPromise().then(function (fulfilled) { }, function (rejected) { });これで、outputPromise は function(fulfilled) または function(rejected) によって制御される Promise になります。単純な意味は、 function(fulfilled) または function(rejected) が文字列、配列、オブジェクトなどの値を返すと、outputPromise のステータスが満たされるということです。 次の例では、defer.resovle() を通じて inputPromise のステータスを fullful に変更すると、コンソール出力がフルフィルメントされることがわかります。それは拒否され、コンソールには拒否されたと出力されます
var Q = require('q'); var defer = Q.defer(); /** * 通过 defer 获得 promise * @private */ function getInputPromise() { return defer.promise; } /** * 当 inputPromise 状态由未完成变成 fulfil 时,调用 function(fulfilled) * 当 inputPromise 状态由未完成变成 rejected 时,调用 function(rejected) * 将 then 返回的 promise 赋给 outputPromise * function(fulfilled) 和 function(rejected) 通过返回字符串将 outputPromise 的状态由 * 未完成改变为 fulfilled * @private */ var outputPromise = getInputPromise().then(function (fulfilled) { return 'fulfilled'; }, function (rejected) { return 'rejected'; }); /** * 当 outputPromise 状态由未完成变成 fulfil 时,调用 function(fulfilled),控制台打印 'fulfilled: fulfilled'。 * 当 outputPromise 状态由未完成变成 rejected, 调用 function(rejected), 控制台打印 'rejected: rejected'。 */ outputPromise.then(function (fulfilled) { console.log('fulfilled: ' + fulfilled); }, function (rejected) { console.log('rejected: ' + rejected); }); /** * 将 inputPromise 的状态由未完成变成 rejected */ defer.reject(); // 输出 fulfilled: rejected /** * 将 inputPromise 的状态由未完成变成 fulfilled */ //defer.resolve(); // 输出 fulfilled: fulfilled
当 function(fulfilled) 或者 function(rejected) 抛出异常时,那么 outputPromise 的状态就会变成 rejected
var Q = require('q'); var fs = require('fs'); var defer = Q.defer(); /** * 通过 defer 获得 promise * @private */ function getInputPromise() { return defer.promise; } /** * 当 inputPromise 状态由未完成变成 fulfil 时,调用 function(fulfilled) * 当 inputPromise 状态由未完成变成 rejected 时,调用 function(rejected) * 将 then 返回的 promise 赋给 outputPromise * function(fulfilled) 和 function(rejected) 通过抛出异常将 outputPromise 的状态由 * 未完成改变为 reject * @private */ var outputPromise = getInputPromise().then(function (fulfilled) { throw new Error('fulfilled'); }, function (rejected) { throw new Error('rejected'); }); /** * 当 outputPromise 状态由未完成变成 fulfil 时,调用 function(fulfilled)。 * 当 outputPromise 状态由未完成变成 rejected, 调用 function(rejected)。 */ outputPromise.then(function (fulfilled) { console.log('fulfilled: ' + fulfilled); }, function (rejected) { console.log('rejected: ' + rejected); }); /** * 将 inputPromise 的状态由未完成变成 rejected */ defer.reject(); // 控制台打印 rejected [Error:rejected] /** * 将 inputPromise 的状态由未完成变成 fulfilled */ //defer.resolve(); // 控制台打印 rejected [Error:fulfilled]
当 function(fulfilled) 或者 function(rejected) 返回一个 promise 时,outputPromise 就会成为这个新的 promise.
这样做的意义在于聚合结果 (Q.all),管理延时,异常恢复等等
比如说我们想要读取一个文件的内容,然后把这些内容打印出来。可能会写出这样的代码:
// 错误的写法 var outputPromise = getInputPromise().then(function (fulfilled) { fs.readFile('test.txt', 'utf8', function (err, data) { return data; }); });
然而这样写是错误的,因为 function(fulfilled) 并没有返回任何值。需要下面的方式:
var Q = require('q'); var fs = require('fs'); var defer = Q.defer(); /** * 通过 defer 获得promise * @private */ function getInputPromise() { return defer.promise; } /** * 当 inputPromise 状态由未完成变成 fulfil时,调用 function(fulfilled) * 当 inputPromise 状态由未完成变成 rejected时,调用 function(rejected) * 将 then 返回的 promise 赋给 outputPromise * function(fulfilled) 将新的 promise 赋给 outputPromise * 未完成改变为 reject * @private */ var outputPromise = getInputPromise().then(function (fulfilled) { var myDefer = Q.defer(); fs.readFile('test.txt', 'utf8', function (err, data) { if (!err && data) { myDefer.resolve(data); } }); return myDefer.promise; }, function (rejected) { throw new Error('rejected'); }); /** * 当 outputPromise 状态由未完成变成 fulfil 时,调用 function(fulfilled),控制台打印 test.txt 文件内容。 * */ outputPromise.then(function (fulfilled) { console.log(fulfilled); }, function (rejected) { console.log(rejected); }); /** * 将 inputPromise 的状态由未完成变成 rejected */ //defer.reject(); /** * 将 inputPromise 的状态由未完成变成 fulfilled */ defer.resolve(); // 控制台打印出 test.txt 的内容
方法传递
方法传递有些类似于 Java 中的 try 和 catch。当一个异常没有响应的捕获时,这个异常会接着往下传递
方法传递的含义是当一个状态没有响应的回调函数,就会沿着 then 往下找
没有提供 function(rejected)
var outputPromise = getInputPromise().then(function (fulfilled) { })
如果 inputPromise 的状态由未完成变成 rejected, 此时对 rejected 的处理会由 outputPromise 来完成
var Q = require('q'); var fs = require('fs'); var defer = Q.defer(); /** * 通过defer获得promise * @private */ function getInputPromise() { return defer.promise; } /** * 当 inputPromise 状态由未完成变成 fulfil 时,调用 function(fulfilled) * 当 inputPromise 状态由未完成变成 rejected 时,这个 rejected 会传向 outputPromise */ var outputPromise = getInputPromise().then(function (fulfilled) { return 'fulfilled' }); outputPromise.then(function (fulfilled) { console.log('fulfilled: ' + fulfilled); }, function (rejected) { console.log('rejected: ' + rejected); }); /** * 将 inputPromise 的状态由未完成变成 rejected */ defer.reject('inputpromise rejected'); // 控制台打印 rejected: inputpromise rejected /** * 将 inputPromise的状态由未完成变成fulfilled */ //defer.resolve();
没有提供 function(fulfilled)
var outputPromise = getInputPromise().then(null, function (rejected) { })
如果 inputPromise 的状态由未完成变成 fulfilled, 此时对 fulfil 的处理会由 outputPromise 来完成
var Q = require('q'); var fs = require('fs'); var defer = Q.defer(); /** * 通过defer获得promise * @private */ function getInputPromise() { return defer.promise; } /** * 当 inputPromise 状态由未完成变成 fulfil时,传递给 outputPromise * 当 inputPromise 状态由未完成变成 rejected时,调用 function(rejected) * function(fulfilled) 将新的 promise 赋给 outputPromise * 未完成改变为 reject * @private */ var outputPromise = getInputPromise().then(null, function (rejected) { return 'rejected'; }); outputPromise.then(function (fulfilled) { console.log('fulfilled: ' + fulfilled); }, function (rejected) { console.log('rejected: ' + rejected); }); /** * 将 inputPromise 的状态由未完成变成 rejected */ // defer.reject('inputpromise rejected'); /** * 将 inputPromise 的状态由未完成变成fulfilled */ defer.resolve('inputpromise fulfilled'); // 控制台打印fulfilled: inputpromise fulfilled
可以使用 fail(function(error)) 来专门针对错误处理,而不是使用 then(null,function(error))
var outputPromise = getInputPromise().fail(function (error) { })
看这个例子:
var Q = require('q'); var fs = require('fs'); var defer = Q.defer(); /** * 通过defer获得promise * @private */ function getInputPromise() { return defer.promise; } /** * 当 inputPromise 状态由未完成变成 fulfil 时,调用 then(function(fulfilled)) * 当 inputPromise 状态由未完成变成 rejected 时,调用 fail(function(error)) * function(fulfilled) 将新的 promise 赋给 outputPromise * 未完成改变为reject * @private */ var outputPromise = getInputPromise().then(function (fulfilled) { return fulfilled; }).fail(function (error) { console.log('fail: ' + error); }); /** * 将 inputPromise 的状态由未完成变成 rejected */ defer.reject('inputpromise rejected');// 控制台打印 fail: inputpromise rejected /** * 将 inputPromise 的状态由未完成变成 fulfilled */ //defer.resolve('inputpromise fulfilled');
可以使用 progress(function (progress)) 来专门针对进度信息进行处理,而不是使用 then(function (success) { }, function (error) { }, function (progress) { })
var Q = require('q'); var defer = Q.defer(); /** * 获取初始 promise * @private */ function getInitialPromise() { return defer.promise; } /** * 为 promise 设置 progress 信息处理函数 */ var outputPromise = getInitialPromise().then(function (success) { }).progress(function (progress) { console.log(progress); }); defer.notify(1); defer.notify(2); // 控制台打印 1,2
promise 链
promise 链提供了一种让函数顺序执行的方法
函数顺序执行是很重要的一个功能。比如知道用户名,需要根据用户名从数据库中找到相应的用户,然后将用户信息传给下一个函数进行处理
var Q = require('q'); var defer = Q.defer(); // 一个模拟数据库 var users = [{ 'name': 'andrew', 'passwd': 'password' }]; function getUsername() { return defer.promise; } function getUser(username) { var user; users.forEach(function (element) { if (element.name === username) { user = element; } }); return user; } // promise 链 getUsername().then(function (username) { return getUser(username); }).then(function (user) { console.log(user); }); defer.resolve('andrew');
我们通过两个 then 达到让函数顺序执行的目的。
then 的数量其实是没有限制的。当然,then 的数量过多,要手动把他们链接起来是很麻烦的。比如
foo(initialVal).then(bar).then(baz).then(qux)
这时我们需要用代码来动态制造 promise 链
var funcs = [foo, bar, baz, qux] var result = Q(initialVal) funcs.forEach(function (func) { result = result.then(func) }) return result
当然,我们可以再简洁一点
var funcs = [foo, bar, baz, qux] funcs.reduce(function (pre, current),Q(initialVal){ return pre.then(current) })
看一个具体的例子
function foo(result) { console.log(result); return result + result; } // 手动链接 Q('hello').then(foo).then(foo).then(foo); // 控制台输出: hello // hellohello // hellohellohello // 动态链接 var funcs = [foo, foo, foo]; var result = Q('hello'); funcs.forEach(function (func) { result = result.then(func); }); // 精简后的动态链接 funcs.reduce(function (prev, current) { return prev.then(current); }, Q('hello'));
对于 promise 链,最重要的是需要理解为什么这个链能够顺序执行。如果能够理解这点,那么以后自己写 promise 链可以说是轻车熟路啊
promise 组合
回到我们一开始读取文件内容的例子。如果现在让我们把它改写成 promise 链,是不是很简单呢?
var Q = require('q'), fs = require('fs'); function printFileContent(fileName) { return function () { var defer = Q.defer(); fs.readFile(fileName, 'utf8', function (err, data) { if (!err && data) { console.log(data); defer.resolve(); } }) return defer.promise; } } // 手动链接 printFileContent('sample01.txt')() .then(printFileContent('sample02.txt')) .then(printFileContent('sample03.txt')) .then(printFileContent('sample04.txt')); // 控制台顺序打印 sample01 到 sample04 的内容
很有成就感是不是。然而如果仔细分析,我们会发现为什么要他们顺序执行呢,如果他们能够并行执行不是更好吗? 我们只需要在他们都执行完成之后,得到他们的执行结果就可以了
我们可以通过 Q.all([promise1,promise2...]) 将多个 promise 组合成一个 promise 返回。 注意:
1. 当 all 里面所有的 promise 都 fulfil 时,Q.all 返回的 promise 状态变成 fulfil
2. 当任意一个 promise 被 reject 时,Q.all 返回的 promise 状态立即变成 reject
我们来把上面读取文件内容的例子改成并行执行吧
var Q = require('q'); var fs = require('fs'); /** *读取文件内容 *@private */ function printFileContent(fileName) { // Todo: 这段代码不够简洁。可以使用 Q.denodeify 来简化 var defer = Q.defer(); fs.readFile(fileName, 'utf8', function (err, data) { if (!err && data) { console.log(data); defer.resolve(fileName + ' success '); } else { defer.reject(fileName + ' fail '); } }) return defer.promise; } Q.all([printFileContent('sample01.txt'), printFileContent('sample02.txt'), printFileContent('sample03.txt'), printFileContent('sample04.txt')]) .then(function (success) { console.log(success); }); // 控制台打印各个文件内容 顺序不一定
现在知道 Q.all 会在任意一个 promise 进入 reject 状态后立即进入 reject 状态。如果我们需要等到所有的 promise 都发生状态后(有的 fulfil, 有的 reject),再转换 Q.all 的状态, 这时我们可以使用 Q.allSettled
var Q = require('q'), fs = require('fs'); /** *读取文件内容 *@private */ function printFileContent(fileName) { // Todo: 这段代码不够简洁。可以使用Q.denodeify来简化 var defer = Q.defer(); fs.readFile(fileName, 'utf8', function (err, data) { if (!err && data) { console.log(data); defer.resolve(fileName + ' success '); } else { defer.reject(fileName + ' fail '); } }) return defer.promise; } Q.allSettled([printFileContent('nosuchfile.txt'), printFileContent('sample02.txt'), printFileContent('sample03.txt'), printFileContent('sample04.txt')]) .then(function (results) { results.forEach( function (result) { console.log(result.state); } ); });
结束 promise 链
通常,对于一个 promise 链,有两种结束的方式。第一种方式是返回最后一个 promise
如 return foo().then(bar);
第二种方式就是通过 done 来结束 promise 链
如 foo().then(bar).done()
为什么需要通过 done 来结束一个 promise 链呢? 如果在我们的链中有错误没有被处理,那么在一个正确结束的 promise 链中,这个没被处理的错误会通过异常抛出
var Q = require('q'); function getPromise(msg, timeout, opt) { var defer = Q.defer(); setTimeout(function () { console.log(msg); if (opt) defer.reject(msg); else defer.resolve(msg); }, timeout); return defer.promise; } /** * 没有用 done() 结束的 promise 链 * 由于 getPromse('2',2000,'opt') 返回 rejected, getPromise('3',1000) 就没有执行 * 然后这个异常并没有任何提醒,是一个潜在的 bug */ getPromise('1', 3000) .then(function () { return getPromise('2', 2000, 'opt') }) .then(function () { return getPromise('3', 1000) }); /** * 用 done() 结束的 promise 链 * 有异常抛出 */ getPromise('1', 3000) .then(function () { return getPromise('2', 2000, 'opt') }) .then(function () { return getPromise('3', 1000) }) .done();
附录:一个 Promise 简单的应用(Node.js笔记(5)promise)
附:Promises/A+ 规范
promise 代表一个异步操作的最终结果。主要通过 promise 的 then 方法订阅其最终结果的处理回调函数,和订阅因某原因无法成功获取最终结果的处理回调函数。
更对详细见:Promises/A+
A 与 A+ 的不同点
A+ 规范通过术语 thenable 来区分 promise 对象
A+ 定义 onFulfilled/onRejectd 必须是作为函数来调用,而且调用过程必须是异步的
A+ 严格定义了 then 方法链式调用时,onFulfilled/onRejectd 的调用顺序
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上がノードの操作方法とコールバック関数の代わりにPromiseを使用する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

JavaScriptは、Webページのインタラクティブ性とダイナミズムを向上させるため、現代のWebサイトの中心にあります。 1)ページを更新せずにコンテンツを変更できます。2)Domapiを介してWebページを操作する、3)アニメーションやドラッグアンドドロップなどの複雑なインタラクティブ効果、4)ユーザーエクスペリエンスを改善するためのパフォーマンスとベストプラクティスを最適化します。

CおよびJavaScriptは、WebAssemblyを介して相互運用性を実現します。 1)CコードはWebAssemblyモジュールにコンパイルされ、JavaScript環境に導入され、コンピューティングパワーが強化されます。 2)ゲーム開発では、Cは物理エンジンとグラフィックスレンダリングを処理し、JavaScriptはゲームロジックとユーザーインターフェイスを担当します。

JavaScriptは、Webサイト、モバイルアプリケーション、デスクトップアプリケーション、サーバー側のプログラミングで広く使用されています。 1)Webサイト開発では、JavaScriptはHTMLおよびCSSと一緒にDOMを運用して、JQueryやReactなどのフレームワークをサポートします。 2)ReactNativeおよびIonicを通じて、JavaScriptはクロスプラットフォームモバイルアプリケーションを開発するために使用されます。 3)電子フレームワークにより、JavaScriptはデスクトップアプリケーションを構築できます。 4)node.jsを使用すると、JavaScriptがサーバー側で実行され、高い並行リクエストをサポートします。

Pythonはデータサイエンスと自動化により適していますが、JavaScriptはフロントエンドとフルスタックの開発により適しています。 1. Pythonは、データ処理とモデリングのためにNumpyやPandasなどのライブラリを使用して、データサイエンスと機械学習でうまく機能します。 2。Pythonは、自動化とスクリプトにおいて簡潔で効率的です。 3. JavaScriptはフロントエンド開発に不可欠であり、動的なWebページと単一ページアプリケーションの構築に使用されます。 4. JavaScriptは、node.jsを通じてバックエンド開発において役割を果たし、フルスタック開発をサポートします。

CとCは、主に通訳者とJITコンパイラを実装するために使用されるJavaScriptエンジンで重要な役割を果たします。 1)cは、JavaScriptソースコードを解析し、抽象的な構文ツリーを生成するために使用されます。 2)Cは、Bytecodeの生成と実行を担当します。 3)Cは、JITコンパイラを実装し、実行時にホットスポットコードを最適化およびコンパイルし、JavaScriptの実行効率を大幅に改善します。

現実世界でのJavaScriptのアプリケーションには、フロントエンドとバックエンドの開発が含まれます。 1)DOM操作とイベント処理を含むTODOリストアプリケーションを構築して、フロントエンドアプリケーションを表示します。 2)node.jsを介してRestfulapiを構築し、バックエンドアプリケーションをデモンストレーションします。

Web開発におけるJavaScriptの主な用途には、クライアントの相互作用、フォーム検証、非同期通信が含まれます。 1)DOM操作による動的なコンテンツの更新とユーザーインタラクション。 2)ユーザーエクスペリエンスを改善するためにデータを提出する前に、クライアントの検証が実行されます。 3)サーバーとのリフレッシュレス通信は、AJAXテクノロジーを通じて達成されます。

JavaScriptエンジンが内部的にどのように機能するかを理解することは、開発者にとってより効率的なコードの作成とパフォーマンスのボトルネックと最適化戦略の理解に役立つためです。 1)エンジンのワークフローには、3つの段階が含まれます。解析、コンパイル、実行。 2)実行プロセス中、エンジンはインラインキャッシュや非表示クラスなどの動的最適化を実行します。 3)ベストプラクティスには、グローバル変数の避け、ループの最適化、constとletsの使用、閉鎖の過度の使用の回避が含まれます。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

VSCode Windows 64 ビットのダウンロード
Microsoft によって発売された無料で強力な IDE エディター

ZendStudio 13.5.1 Mac
強力な PHP 統合開発環境

MantisBT
Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。

メモ帳++7.3.1
使いやすく無料のコードエディター

mPDF
mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

ホットトピック









