导读
上周讲了promise用法,这周我们讲一下promise实战中可能出现得一些易错点,如果对promise基础不是很了解可以去看我的上一篇文章 promise入门
常见问题一 :回调地狱式用法
remotedb.allDocs({
include_docs: true,
attachments: true
}).then(function (result) {
var docs = result.rows;
docs.forEach(function(element) {
localdb.put(element.doc).then(function(response) {
alert("Pulled doc with id " + element.doc._id + " and added to local db.");
}).catch(function (err) {
if (err.status == 409) {
localdb.get(element.doc._id).then(function (resp) {
localdb.remove(resp._id, resp._rev).then(function (resp) {}
}
})
复制代码
promise得新特性之一就是解决回调地狱问题,所以我们可以换个更优雅得写法达到上段代码的效果
remotedb.allDocs(...).then(function (resultOfAllDocs) {
return localdb.put(...);
}).then(function (resultOfPut) {
return localdb.get(...);
}).then(function (resultOfGet) {
return localdb.put(...);
}).catch(function (err) {
console.log(err);
});
复制代码
######常见问题二 : 用了 promises
后怎么用 forEach
?
/ I want to remove() all docs
db.allDocs({include_docs: true}).then(function (result) {
result.rows.forEach(function (row) {
db.remove(row.doc);
});
}).then(function () {
// I naively believe all docs have been removed() now!
});
复制代码
这份代码有什么问题?问题在于第一个then
函数实际上返回的是 undefined
(入门篇讲过我们需要手动return
),这意味着第二个方法不会等待所有 documents 都执行 db.remove()
方法(因为后一个then
接收到的undefind
并没有类似于promise
实例状态ejected/fullfilled
)。所以他不会等待任何事情,并且可能会在任意数量的文档被删除后执行! 这里我们需要用 Promise.all()
db.allDocs({include_docs: true}).then(function (result) {
return Promise.all(result.rows.map(function (row) {
return db.remove(row.doc);
}));
}).then(function (arrayOfResults) {
// All docs have really been removed() now!
});
复制代码
上面的代码是什么意思呢?大体来说,Promise.all()
会以一个 promises
数组为输入,并且返回一个新的 promise
。这个新的 promise
会在数组中所有的 promises
都成功返回后才返回。一旦数组中的 promise
任意一个返回错误,Promise.all()
也会返回错误,他是异步版的 for
循环。
常见问题三 : 忘记使用 .catch()
我们在使用程序的时候,难免会出现问题,如果不适用catch
找起问题来会特别麻烦
somePromise().then(function () {
return anotherPromise();
}).then(function () {
return yetAnotherPromise();
}).catch(
err=>{
thorw new Error(err)
});
复制代码
常见问题四 :使用副作用调用而非返回
没有return
返回新的promise
就会默认返回一个undefind
,这就是副作用
somePromise().then(function () {
someOtherPromise();
}).then(function () {
// Gee, I hope someOtherPromise() has resolved!
// Spoiler alert: it hasn't.
});
复制代码
每一个 promise
都会提供给你一个 then()
函数 (或是 catch()
,实际上只是 then(null, ...)
的语法糖)。当我们在 then() 函数内部时:
somePromise().then(function () {
// 这里可以做什么???
});
复制代码
我们可以在这里做三件事情
return
另一个promise
return
一个同步的值 (或者undefined
)throw
一个同步异常
我们来看一下这三种事情
-
返回另一个 promise
getUserByName('nolan').then(function (user) {
return getUserAccountById(user.id);
}).then(function (userAccount) {
// I got a user account!
});
复制代码
注意到我是 return
第二个 promise,这个 return
非常重要。如果我没有写 return
,getUserAccountById()
就会成为一个副作用,并且下一个函数将会接收到 undefined
而非新的 promise
。
- ######返回一个同步值 (或者 undefined) 返回
undefined
通常是错误的,但是返回一个同步值实际上是将同步代码包裹为promise
风格代码的一种非常赞的手段。
getUserByName('nolan').then(function (user) {
if (inMemoryCache[user.id]) {
return inMemoryCache[user.id]; // returning a synchronous value!
}
return getUserAccountById(user.id); // returning a promise!
}).then(function (userAccount) {
// I got a user account!
});
复制代码
是不是很赞?第二个函数不需要关心 userAccount
是从同步方法还是异步方法中获取的,并且第一个函数可以非常自由的返回一个同步或者异步值。
不幸的是,有一个不便的现实是在 JavaScript 中无返回值函数在技术上是返回 undefined
,这就意味着当你本意是返回某些值时,你很容易会不经意间引入副作用。
出于这个原因,我个人养成了在 then()
函数内部 永远返回或抛出 的习惯。我建议你也这样做。
####进阶错误 ######不知道 Promise.resolve()/Promise.reject(); 我们会经常这么使用promise
new Promise(function (resolve, reject) {
resolve(someSynchronousValue);
}).then(/* ... */);
复制代码
使用 Promise.resolve()
会更加简洁
Promise.resolve(someSynchronousValue).then(/* ... */);
复制代码
我们应该在所有 promise 形式的 API 接口中这样使用它
function somePromiseAPI() {
return Promise.resolve().then(function () {
doSomethingThatMayThrow();
return 'foo';
}).then(/* ... */);
}
复制代码
任何有可能 throw 同步异常的代码都是一个后续会导致几乎无法调试异常的潜在因素。但是如果你将所有代码都使用 Promise.resolve() 封装,那么你总是可以在之后使用 catch() 来捕获它。 类似的,还有 Promise.reject() 你可以用来返回一个立刻返回失败的 promise。
Promise.reject(new Error('some awful error'));
复制代码
promises 穿透
Promise.resolve('foo').then(
Promise.resolve('bar')
).then(function (result) {
console.log(result); //foo
});
复制代码
之所以会打印foo而不是bar,是因为我们 Promise.resolve('bar')
这段代码有问题,这段代码返回的是一个promise
,但我们并没有return
Promise.resolve('foo').then(function () {
return Promise.resolve('bar');
}).then(function (result) {
console.log(result);
});
复制代码
当然promise还有一些高级的用法,大家可以去读一下 promise,我的实例代码全部是这篇文章的,作者是一个外国大牛,有兴趣的可以去看一下