Note

We have a problem with promises

http://fex.baidu.com/blog/2015/07/we-have-a-problem-with-promises/ http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html

一、新手错误

Promise with 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!
});

这份代码有什么问题?问题在于第一个函数实际上返回的是 undefined,这意味着第二个方法不会等待所有 documents 都执行 db.remove()。实际上他不会等待任何事情,并且可能会在任意数量的文档被删除后执行!

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!
});

使用副作用调用而非返回

// 错误
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 () {
  // I'm inside a then() function!
});

我们可以做什么呢?有三种事情:

  1. return 另一个 promise

  2. return 一个同步的值 (或者 undefined)

  3. throw 一个同步异常

getUserByName('nolan').then(function (user) {
  if (user.isLoggedOut()) {
    throw new Error('user logged out!'); // throwing a synchronous error!
  }
  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!
}).catch(function (err) {
  // Boo, I got an error!
});

上面这段代码非常重要!

二、谜题揭晓

puzzle 1

doSomething().then(function () {
  return doSomethingElse();
}).then(finalHandler);
doSomething
|-----------------|
                  doSomethingElse(undefined)
                  |------------------|
                                     finalHandler(resultOfDoSomethingElse)
                                     |------------------|

puzzle 2

doSomething().then(function () {
  doSomethingElse();
}).then(finalHandler);
doSomething
|-----------------|
                  doSomethingElse(undefined)
                  |------------------|
                  finalHandler(undefined)
                  |------------------|

puzzle 3

doSomething().then(doSomethingElse())
  .then(finalHandler);
doSomething
|-----------------|
doSomethingElse(undefined)
|---------------------------------|
                  finalHandler(resultOfDoSomething)
                  |------------------|

puzzle 4

doSomething().then(doSomethingElse)
  .then(finalHandler);
doSomething
|-----------------|
                  doSomethingElse(resultOfDoSomething)
                  |------------------|
                                     finalHandler(resultOfDoSomethingElse)
                                     |------------------|
es6 promise