es6 promise用於非同步程式設計。 Promise是非同步程式設計的一種解決方案,比傳統的解決方案「回呼函數和事件」更合理和更強大。 Promise由社群最早提出和實現,ES6將其寫進了語言標準,統一了用法,原生提供了Promise物件。從語法上說,Promise是一個對象,從它可以取得非同步操作的訊息。 Promise提供統一的API,各種非同步操作都可以用同樣的方法處理。
本教學操作環境:windows7系統、ECMAScript 6版、Dell G3電腦。
Promise的含義
Promise是非同步程式設計的一種解決方案,比傳統的解決方案——回呼函數和事件——更合理和更強大。它由社群最早提出和實現,ES6將其寫進了語言標準,統一了用法,原生提供了Promise
物件。
所謂Promise
,簡單說就是一個容器,裡面保存著某個未來才會結束的事件(通常是一個非同步操作)的結果。從語法上說,Promise是一個對象,從它可以取得非同步操作的訊息。 Promise提供統一的API,各種非同步操作都可以用同樣的方法處理。
Promise
物件有以下兩個特點。
(1)物件的狀態不受外界影響。 Promise
物件代表一個非同步操作,有三種狀態:Pending
(進行中)、Resolved
(已完成,又稱為Fulfilled)和Rejected
(已失敗)。只有非同步操作的結果,可以決定目前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是Promise
這個名字的由來,它的英語意思是“承諾”,表示其他手段無法改變。
(2)一旦狀態改變,就不會再變,任何時候都可以得到這個結果。 Promise
物件的狀態改變,只有兩種可能:從Pending
變成Resolved
和從Pending
變成Rejected
。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直維持這個結果。就算改變已經發生了,你再對Promise
物件加入回呼函數,也會立即得到這個結果。這與事件(Event)完全不同,事件的特徵是,如果你錯過了它,再去監聽,是得不到結果的。
有了Promise
對象,就可以將非同步操作以同步操作的流程表達出來,避免了層層嵌套的回呼函數。此外,Promise
物件提供統一的接口,使得控制非同步操作更加容易。
Promise
也有一些缺點。首先,無法取消Promise
,一旦新建它就會立即執行,無法中途取消。其次,如果不設定回呼函數,Promise
內部拋出的錯誤,不會反應到外部。第三,當處於Pending
狀態時,無法得知目前進展到哪一個階段(剛開始還是即將完成)。
如果某些事件不斷地反覆發生,一般來說,使用stream模式是比部署Promise
更好的選擇。
基本用法
ES6規定,Promise物件是一個建構函數,用來產生Promise實例。
下面程式碼創造了一個Promise實例。
var promise = new Promise(function(resolve, reject) { // ... some code if (/* 异步操作成功 */){ resolve(value); } else { reject(error); } });
Promise建構子接受一個函數作為參數,該函數的兩個參數分別是resolve
和reject
。它們是兩個函數,由JavaScript引擎提供,不用自己部署。
resolve
函數的作用是,將Promise物件的狀態從「未完成」變為「成功」(即從Pending變為Resolved),在非同步操作成功時調用,並將非同步操作的結果,作為參數傳遞出去;reject
函數的作用是,將Promise物件的狀態從「未完成」變為「失敗」(即從Pending變為Rejected),在非同步操作失敗時調用,並將非同步操作報出的錯誤,作為參數傳遞出去。
Promise實例產生以後,可以用then
方法分別指定Resolved
狀態與Reject
狀態的回呼函數。
promise.then(function(value) { // success }, function(error) { // failure });
then
方法可以接受兩個回呼函數作為參數。第一個回呼函數是Promise物件的狀態變成Resolved時調用,第二個回呼函數是Promise物件的狀態變成Reject時調用。其中,第二個函數是可選的,不一定要提供。這兩個函數都接受Promise物件傳出的值作為參數。
下面是一個Promise物件的簡單範例。
function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms, 'done'); }); } timeout(100).then((value) => { console.log(value); });
上面程式碼中,timeout
方法傳回一個Promise實例,表示一段時間以後才會發生的結果。過了指定的時間(ms
參數)以後,Promise實例的狀態變成Resolved,就會觸發then
方法綁定的回呼函數。
Promise新建後就會立即執行。
let promise = new Promise(function(resolve, reject) { console.log('Promise'); resolve(); }); promise.then(function() { console.log('Resolved.'); }); console.log('Hi!'); // Promise // Hi! // Resolved
上面代码中,Promise新建后立即执行,所以首先输出的是“Promise”。然后,then
方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以“Resolved”最后输出。
下面是异步加载图片的例子。
function loadImageAsync(url) { return new Promise(function(resolve, reject) { var image = new Image(); image.onload = function() { resolve(image); }; image.onerror = function() { reject(new Error('Could not load image at ' + url)); }; image.src = url; }); }
上面代码中,使用Promise包装了一个图片加载的异步操作。如果加载成功,就调用resolve
方法,否则就调用reject
方法。
下面是一个用Promise对象实现的Ajax操作的例子。
var getJSON = function(url) { var promise = new Promise(function(resolve, reject){ var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.responseType = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler() { if (this.readyState !== 4) { return; } if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; }); return promise; }; getJSON("/posts.json").then(function(json) { console.log('Contents: ' + json); }, function(error) { console.error('出错了', error); });
上面代码中,getJSON
是对XMLHttpRequest对象的封装,用于发出一个针对JSON数据的HTTP请求,并且返回一个Promise对象。需要注意的是,在getJSON
内部,resolve
函数和reject
函数调用时,都带有参数。
如果调用resolve
函数和reject
函数时带有参数,那么它们的参数会被传递给回调函数。reject
函数的参数通常是Error对象的实例,表示抛出的错误;resolve
函数的参数除了正常的值以外,还可能是另一个Promise实例,表示异步操作的结果有可能是一个值,也有可能是另一个异步操作,比如像下面这样。
var p1 = new Promise(function (resolve, reject) { // ... }); var p2 = new Promise(function (resolve, reject) { // ... resolve(p1); })
上面代码中,p1
和p2
都是Promise的实例,但是p2
的resolve
方法将p1
作为参数,即一个异步操作的结果是返回另一个异步操作。
注意,这时p1
的状态就会传递给p2
,也就是说,p1
的状态决定了p2
的状态。如果p1
的状态是Pending
,那么p2
的回调函数就会等待p1
的状态改变;如果p1
的状态已经是Resolved
或者Rejected
,那么p2
的回调函数将会立刻执行。
var p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('fail')), 3000) }) var p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000) }) p2 .then(result => console.log(result)) .catch(error => console.log(error)) // Error: fail
上面代码中,p1
是一个Promise,3秒之后变为rejected
。p2
的状态在1秒之后改变,resolve
方法返回的是p1
。此时,由于p2
返回的是另一个Promise,所以后面的then
语句都变成针对后者(p1
)。又过了2秒,p1
变为rejected
,导致触发catch
方法指定的回调函数。
Promise.prototype.then()
Promise实例具有then
方法,也就是说,then
方法是定义在原型对象Promise.prototype上的。它的作用是为Promise实例添加状态改变时的回调函数。前面说过,then
方法的第一个参数是Resolved状态的回调函数,第二个参数(可选)是Rejected状态的回调函数。
then
方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then
方法后面再调用另一个then
方法。
getJSON("/posts.json").then(function(json) { return json.post; }).then(function(post) { // ... });
上面的代码使用then
方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。
采用链式的then
,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。
getJSON("/post/1.json").then(function(post) { return getJSON(post.commentURL); }).then(function funcA(comments) { console.log("Resolved: ", comments); }, function funcB(err){ console.log("Rejected: ", err); });
上面代码中,第一个then
方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then
方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为Resolved,就调用funcA
,如果状态变为Rejected,就调用funcB
。
如果采用箭头函数,上面的代码可以写得更简洁。
getJSON("/post/1.json").then( post => getJSON(post.commentURL) ).then( comments => console.log("Resolved: ", comments), err => console.log("Rejected: ", err) );
Promise.prototype.catch()
Promise.prototype.catch
方法是.then(null, rejection)
的别名,用于指定发生错误时的回调函数。
getJSON("/posts.json").then(function(posts) { // ... }).catch(function(error) { // 处理 getJSON 和 前一个回调函数运行时发生的错误 console.log('发生错误!', error); });
上面代码中,getJSON
方法返回一个Promise对象,如果该对象状态变为Resolved
,则会调用then
方法指定的回调函数;如果异步操作抛出错误,状态就会变为Rejected
,就会调用catch
方法指定的回调函数,处理这个错误。另外,then
方法指定的回调函数,如果运行中抛出错误,也会被catch
方法捕获。
p.then((val) => console.log("fulfilled:", val)) .catch((err) => console.log("rejected:", err)); // 等同于 p.then((val) => console.log("fulfilled:", val)) .then(null, (err) => console.log("rejected:", err));
下面是一个例子。
var promise = new Promise(function(resolve, reject) { throw new Error('test'); }); promise.catch(function(error) { console.log(error); }); // Error: test
上面代码中,promise
抛出一个错误,就被catch
方法指定的回调函数捕获。注意,上面的写法与下面两种写法是等价的。
// 写法一 var promise = new Promise(function(resolve, reject) { try { throw new Error('test'); } catch(e) { reject(e); } }); promise.catch(function(error) { console.log(error); }); // 写法二 var promise = new Promise(function(resolve, reject) { reject(new Error('test')); }); promise.catch(function(error) { console.log(error); });
比较上面两种写法,可以发现reject
方法的作用,等同于抛出错误。
如果Promise状态已经变成Resolved
,再抛出错误是无效的。
var promise = new Promise(function(resolve, reject) { resolve('ok'); throw new Error('test'); }); promise .then(function(value) { console.log(value) }) .catch(function(error) { console.log(error) }); // ok
上面代码中,Promise在resolve
语句后面,再抛出错误,不会被捕获,等于没有抛出。
Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch
语句捕获。
getJSON("/post/1.json").then(function(post) { return getJSON(post.commentURL); }).then(function(comments) { // some code }).catch(function(error) { // 处理前面三个Promise产生的错误 });
上面代码中,一共有三个Promise对象:一个由getJSON
产生,两个由then
产生。它们之中任何一个抛出的错误,都会被最后一个catch
捕获。
一般来说,不要在then
方法里面定义Reject状态的回调函数(即then
的第二个参数),总是使用catch
方法。
// bad promise .then(function(data) { // success }, function(err) { // error }); // good promise .then(function(data) { //cb // success }) .catch(function(err) { // error });
上面代码中,第二种写法要好于第一种写法,理由是第二种写法可以捕获前面then
方法执行中的错误,也更接近同步的写法(try/catch
)。因此,建议总是使用catch
方法,而不使用then
方法的第二个参数。
跟传统的try/catch
代码块不同的是,如果没有使用catch
方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应。
var someAsyncThing = function() { return new Promise(function(resolve, reject) { // 下面一行会报错,因为x没有声明 resolve(x + 2); }); }; someAsyncThing().then(function() { console.log('everything is great'); });
上面代码中,someAsyncThing
函数产生的Promise对象会报错,但是由于没有指定catch
方法,这个错误不会被捕获,也不会传递到外层代码,导致运行后没有任何输出。注意,Chrome浏览器不遵守这条规定,它会抛出错误“ReferenceError: x is not defined”。
var promise = new Promise(function(resolve, reject) { resolve("ok"); setTimeout(function() { throw new Error('test') }, 0) }); promise.then(function(value) { console.log(value) }); // ok // Uncaught Error: test
上面代码中,Promise指定在下一轮“事件循环”再抛出错误,结果由于没有指定使用try...catch
语句,就冒泡到最外层,成了未捕获的错误。因为此时,Promise的函数体已经运行结束了,所以这个错误是在Promise函数体外抛出的。
Node.js有一个unhandledRejection
事件,专门监听未捕获的reject
错误。
process.on('unhandledRejection', function (err, p) { console.error(err.stack) });
上面代码中,unhandledRejection
事件的监听函数有两个参数,第一个是错误对象,第二个是报错的Promise实例,它可以用来了解发生错误的环境信息。。
需要注意的是,catch
方法返回的还是一个Promise对象,因此后面还可以接着调用then
方法。
var someAsyncThing = function() { return new Promise(function(resolve, reject) { // 下面一行会报错,因为x没有声明 resolve(x + 2); }); }; someAsyncThing() .catch(function(error) { console.log('oh no', error); }) .then(function() { console.log('carry on'); }); // oh no [ReferenceError: x is not defined] // carry on
上面代码运行完catch
方法指定的回调函数,会接着运行后面那个then
方法指定的回调函数。如果没有报错,则会跳过catch
方法。
Promise.resolve() .catch(function(error) { console.log('oh no', error); }) .then(function() { console.log('carry on'); }); // carry on
上面的代码因为没有报错,跳过了catch
方法,直接执行后面的then
方法。此时,要是then
方法里面报错,就与前面的catch
无关了。
catch
方法之中,还能再抛出错误。
var someAsyncThing = function() { return new Promise(function(resolve, reject) { // 下面一行会报错,因为x没有声明 resolve(x + 2); }); }; someAsyncThing().then(function() { return someOtherAsyncThing(); }).catch(function(error) { console.log('oh no', error); // 下面一行会报错,因为y没有声明 y + 2; }).then(function() { console.log('carry on'); }); // oh no [ReferenceError: x is not defined]
上面代码中,catch
方法抛出一个错误,因为后面没有别的catch
方法了,导致这个错误不会被捕获,也不会传递到外层。如果改写一下,结果就不一样了。
someAsyncThing().then(function() { return someOtherAsyncThing(); }).catch(function(error) { console.log('oh no', error); // 下面一行会报错,因为y没有声明 y + 2; }).catch(function(error) { console.log('carry on', error); }); // oh no [ReferenceError: x is not defined] // carry on [ReferenceError: y is not defined]
上面代码中,第二个catch
方法用来捕获,前一个catch
方法抛出的错误。
Promise.all()
Promise.all
方法用于将多个Promise实例,包装成一个新的Promise实例。
var p = Promise.all([p1, p2, p3]);
上面代码中,Promise.all
方法接受一个数组作为参数,p1
、p2
、p3
都是Promise对象的实例,如果不是,就会先调用下面讲到的Promise.resolve
方法,将参数转为Promise实例,再进一步处理。(Promise.all
方法的参数可以不是数组,但必须具有Iterator接口,且返回的每个成员都是Promise实例。)
p
的状态由p1
、p2
、p3
决定,分成两种情况。
(1)只有p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。
(2)只要p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
下面是一个具体的例子。
// 生成一个Promise对象的数组 var promises = [2, 3, 5, 7, 11, 13].map(function (id) { return getJSON("/post/" + id + ".json"); }); Promise.all(promises).then(function (posts) { // ... }).catch(function(reason){ // ... });
上面代码中,promises
是包含6个Promise实例的数组,只有这6个实例的状态都变成fulfilled
,或者其中有一个变为rejected
,才会调用Promise.all
方法后面的回调函数。
下面是另一个例子。
const databasePromise = connectDatabase(); const booksPromise = databaseProimse .then(findAllBooks); const userPromise = databasePromise .then(getCurrentUser); Promise.all([ booksPromise, userPromise ]) .then(([books, user]) => pickTopRecommentations(books, user));
上面代码中,booksPromise
和userPromise
是两个异步操作,只有等到它们的结果都返回了,才会触发pickTopRecommentations
这个回调函数。
Promise.race()
Promise.race
方法同样是将多个Promise实例,包装成一个新的Promise实例。
var p = Promise.race([p1, p2, p3]);
上面代码中,只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数。
Promise.race
方法的参数与Promise.all
方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve
方法,将参数转为 Promise 实例,再进一步处理。
下面是一个例子,如果指定时间内没有获得结果,就将Promise的状态变为reject
,否则变为resolve
。
var p = Promise.race([ fetch('/resource-that-may-take-a-while'), new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('request timeout')), 5000) }) ]) p.then(response => console.log(response)) p.catch(error => console.log(error))
上面代码中,如果5秒之内fetch
方法无法返回结果,变量p
的状态就会变为rejected
,从而触发catch
方法指定的回调函数。
Promise.resolve()
有时需要将现有对象转为Promise对象,Promise.resolve
方法就起到这个作用。
var jsPromise = Promise.resolve($.ajax('/whatever.json'));
上面代码将jQuery生成的deferred
对象,转为一个新的Promise对象。
Promise.resolve
等价于下面的写法。
Promise.resolve('foo') // 等价于 new Promise(resolve => resolve('foo'))
Promise.resolve
方法的参数分成四种情况。
(1)参数是一个Promise实例
如果参数是Promise实例,那么Promise.resolve
将不做任何修改、原封不动地返回这个实例。
(2)参数是一个thenable
对象
thenable
对象指的是具有then
方法的对象,比如下面这个对象。
let thenable = { then: function(resolve, reject) { resolve(42); } };
Promise.resolve
方法会将这个对象转为Promise对象,然后就立即执行thenable
对象的then
方法。
let thenable = { then: function(resolve, reject) { resolve(42); } }; let p1 = Promise.resolve(thenable); p1.then(function(value) { console.log(value); // 42 });
上面代码中,thenable
对象的then
方法执行后,对象p1
的状态就变为resolved
,从而立即执行最后那个then
方法指定的回调函数,输出42。
(3)参数不是具有then
方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then
方法的对象,则Promise.resolve
方法返回一个新的Promise对象,状态为Resolved
。
var p = Promise.resolve('Hello'); p.then(function (s){ console.log(s) });// Hello
上面代码生成一个新的Promise对象的实例p
。由于字符串Hello
不属于异步操作(判断方法是它不是具有then方法的对象),返回Promise实例的状态从一生成就是Resolved
,所以回调函数会立即执行。Promise.resolve
方法的参数,会同时传给回调函数。
(4)不带有任何参数
Promise.resolve
方法允许调用时不带参数,直接返回一个Resolved
状态的Promise对象。
所以,如果希望得到一个Promise对象,比较方便的方法就是直接调用Promise.resolve
方法。
var p = Promise.resolve(); p.then(function () { // ... });
上面代码的变量p
就是一个Promise对象。
需要注意的是,立即resolve
的Promise对象,是在本轮“事件循环”(event loop)的结束时,而不是在下一轮“事件循环”的开始时。
setTimeout(function () { console.log('three'); }, 0); Promise.resolve().then(function () { console.log('two'); }); console.log('one'); // one // two // three
上面代码中,setTimeout(fn, 0)
在下一轮“事件循环”开始时执行,Promise.resolve()
在本轮“事件循环”结束时执行,console.log(’one‘)
则是立即执行,因此最先输出。
Promise.reject()
Promise.reject(reason)
方法也会返回一个新的Promise实例,该实例的状态为rejected
。它的参数用法与Promise.resolve
方法完全一致。
var p = Promise.reject('出错了'); // 等同于 var p = new Promise((resolve, reject) => reject('出错了')) p.then(null, function (s){ console.log(s) }); // 出错了
上面代码生成一个Promise对象的实例p
,状态为rejected
,回调函数会立即执行。
两个有用的附加方法
ES6的Promise API提供的方法不是很多,有些有用的方法可以自己部署。下面介绍如何部署两个不在ES6之中、但很有用的方法。
done()
Promise对象的回调链,不管以then
方法或catch
方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。因此,我们可以提供一个done
方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。
asyncFunc() .then(f1) .catch(r1) .then(f2) .done();
它的实现代码相当简单。
Promise.prototype.done = function (onFulfilled, onRejected) { this.then(onFulfilled, onRejected) .catch(function (reason) { // 抛出一个全局错误 setTimeout(() => { throw reason }, 0); }); };
从上面代码可见,done
方法的使用,可以像then
方法那样用,提供Fulfilled
和Rejected
状态的回调函数,也可以不提供任何参数。但不管怎样,done
都会捕捉到任何可能出现的错误,并向全局抛出。
finally()
finally
方法用于指定不管Promise对象最后状态如何,都会执行的操作。它与done
方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。
下面是一个例子,服务器使用Promise处理请求,然后使用finally
方法关掉服务器。
server.listen(0) .then(function () { // run test }) .finally(server.stop);
它的实现也很简单。
Promise.prototype.finally = function (callback) { let P = this.constructor; return this.then( value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => { throw reason }) ); };
上面代码中,不管前面的Promise是fulfilled
还是rejected
,都会执行回调函数callback
。
应用
加载图片
我们可以将图片的加载写成一个Promise
,一旦加载完成,Promise
的状态就发生变化。
const preloadImage = function (path) { return new Promise(function (resolve, reject) { var image = new Image(); image.onload = resolve; image.onerror = reject; image.src = path; }); };
Generator函数与Promise的结合
使用Generator函数管理流程,遇到异步操作的时候,通常返回一个Promise
对象。
function getFoo () { return new Promise(function (resolve, reject){ resolve('foo'); }); } var g = function* () { try { var foo = yield getFoo(); console.log(foo); } catch (e) { console.log(e); } }; function run (generator) { var it = generator(); function go(result) { if (result.done) return result.value; return result.value.then(function (value) { return go(it.next(value)); }, function (error) { return go(it.throw(error)); }); } go(it.next()); } run(g);
上面代码的Generator函数g
之中,有一个异步操作getFoo
,它返回的就是一个Promise
对象。函数run
用来处理这个Promise
对象,并调用下一个next
方法。
Promise.try()
实际开发中,经常遇到一种情况:不知道或者不想区分,函数f
是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f
是否包含异步操作,都用then
方法指定下一步流程,用catch
方法处理f
抛出的错误。一般就会采用下面的写法。
Promise.resolve().then(f)
上面的写法有一个缺点,就是如果f
是同步函数,那么它会在下一轮事件循环执行。
const f = () => console.log('now'); Promise.resolve().then(f); console.log('next'); // next // now
上面代码中,函数f
是同步的,但是用 Promise 包装了以后,就变成异步执行了。
那么有没有一种方法,让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API 呢?回答是可以的,并且还有两种写法。第一种写法是用async
函数来写。
const f = () => console.log('now'); (async () => f())(); console.log('next'); // now // next
上面代码中,第一行是一个立即执行的匿名函数,会立即执行里面的async
函数,因此如果f
是同步的,就会得到同步的结果;如果f
是异步的,就可以用then
指定下一步,就像下面的写法。
(async () => f())() .then(...)
需要注意的是,async () => f()
会吃掉f()
抛出的错误。所以,如果想捕获错误,要使用promise.catch
方法。
(async () => f())() .then(...) .catch(...)
第二种写法是使用new Promise()
。
const f = () => console.log('now'); ( () => new Promise( resolve => resolve(f()) ) )(); console.log('next'); // now // next
上面代码也是使用立即执行的匿名函数,执行new Promise()
。这种情况下,同步函数也是同步执行的。
鉴于这是一个很常见的需求,所以现在有一个提案,提供Promise.try
方法替代上面的写法。
const f = () => console.log('now'); Promise.try(f); console.log('next'); // now // next
事实上,Promise.try
存在已久,Promise 库Bluebird
、Q
和when
,早就提供了这个方法。
由于Promise.try
为所有操作提供了统一的处理机制,所以如果想用then
方法管理流程,最好都用Promise.try
包装一下。这样有许多好处,其中一点就是可以更好地管理异常。
function getUsername(userId) { return database.users.get({id: userId}) .then(function(user) { return user.name; }); }
上面代码中,database.users.get()
返回一个 Promise 对象,如果抛出异步错误,可以用catch
方法捕获,就像下面这样写。
database.users.get({id: userId}) .then(...) .catch(...)
但是database.users.get()
可能还会抛出同步错误(比如数据库连接错误,具体要看实现方法),这时你就不得不用try...catch
去捕获。
try { database.users.get({id: userId}) .then(...) .catch(...) } catch (e) { // ... }
上面这样的写法就很笨拙了,这时就可以统一用promise.catch()
捕获所有同步和异步的错误。
Promise.try(database.users.get({id: userId})) .then(...) .catch(...)
事实上,Promise.try
就是模拟try
代码块,就像promise.catch
模拟的是catch
代码块。
【推荐学习:javascript视频教程】
以上是es6中promise什麼時候用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

HTML和React的關係是前端開發的核心,它們共同構建現代Web應用的用戶界面。 1)HTML定義內容結構和語義,React通過組件化構建動態界面。 2)React組件使用JSX語法嵌入HTML,實現智能渲染。 3)組件生命週期管理HTML渲染,根據狀態和屬性動態更新。 4)使用組件優化HTML結構,提高可維護性。 5)性能優化包括避免不必要渲染,使用key屬性,保持組件單一職責。

React是構建交互式前端體驗的首選工具。 1)React通過組件化和虛擬DOM簡化UI開發。 2)組件分為函數組件和類組件,函數組件更簡潔,類組件提供更多生命週期方法。 3)React的工作原理依賴虛擬DOM和調和算法,提高性能。 4)狀態管理使用useState或this.state,生命週期方法如componentDidMount用於特定邏輯。 5)基本用法包括創建組件和管理狀態,高級用法涉及自定義鉤子和性能優化。 6)常見錯誤包括狀態更新不當和性能問題,調試技巧包括使用ReactDevTools和優

React是一個用於構建用戶界面的JavaScript庫,其核心是組件化和狀態管理。 1)通過組件化和狀態管理簡化UI開發。 2)工作原理包括調和和渲染,優化可通過React.memo和useMemo實現。 3)基本用法是創建並渲染組件,高級用法包括使用Hooks和ContextAPI。 4)常見錯誤如狀態更新不當,可使用ReactDevTools調試。 5)性能優化包括使用React.memo、虛擬化列表和CodeSplitting,保持代碼可讀性和可維護性是最佳實踐。

React通過JSX與HTML結合,提升用戶體驗。 1)JSX嵌入HTML,使開發更直觀。 2)虛擬DOM機制優化性能,減少DOM操作。 3)組件化管理UI,提高可維護性。 4)狀態管理和事件處理增強交互性。

React組件可以通過函數或類定義,封裝UI邏輯並通過props接受輸入數據。 1)定義組件:使用函數或類,返回React元素。 2)渲染組件:React調用render方法或執行函數組件。 3)復用組件:通過props傳遞數據,構建複雜UI。組件的生命週期方法允許在不同階段執行邏輯,提升開發效率和代碼可維護性。

React嚴格模式是一種開發工具,可通過激活其他檢查和警告來突出反應應用中的潛在問題。它有助於識別遺產代碼,不安全的生命週期和副作用,鼓勵現代反應實踐。

本文討論了React的對帳過程,詳細介紹了它如何有效地更新DOM。關鍵步驟包括觸發對帳,創建虛擬DOM,使用擴散算法以及應用最小的DOM更新。它還覆蓋了經家


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SublimeText3 Linux新版
SublimeText3 Linux最新版

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

SublimeText3漢化版
中文版,非常好用