转载请注明出处: http://qiudeqing.com/javascript/2015/06/24/promise.html

Promise的核心思想是用promise对象表示一个异步操作的结果. Promise提供了很好的异步操作管理.已经成为ES6官方标准.

ES6提供了原生Promise支持。Promise对象用于处理延迟和异步任务,一个promise对象有一下几种状态:

使用方法

new Promise(executor);
new Promise(function (resolve, reject) {});

executor:执行任务的函数,接受两个参数,任务完成时进行调用。调用第一个resolve将promise设置到fulfilled状态;第二个reject将promise设置到rejected状态

Description

Promise表示promise创建时还不一定知道的一个值的代理。可以为这个操作的成功或者失败绑定处理逻辑。这样就可以让异步方法像同步方法那样返回值:异步方法返回的promise在将来某个时刻会有值。

一个pending的promise状态变为fulfilled时可以传递一个值,变为rejected时可以传递原因。在发生状态变化之前所有then传递的参数都会放入队列。(当promise在绑定处理函数之前已经fulfilled或者rejected。对应的处理函数会直接调用,所以异步操作完成和函数绑定的顺序不影响程序逻辑)

Promise.prototype.thenPromise.prototype.catch都会返回promise对象,所以可以进行链式调用。

promises

方法

Promise.all(iterable)

参数是一个iterable对象,如数组。

Promise.all()方法返回一个promise,当参数里面的所有promise resolved之后这个返回的promise才会resolve。

参数里promise返回的值将按顺序放到数组中传递给最终的处理函数。如果iterable对象里某个值不是promise,会自动使用Promise.resolve进行转换。

如果iterable对象里任何一个promise进入reject状态。返回的promise对象立刻以这个rejected对象的值进入rejected状态,丢弃其他所有promise。

var promise = Promise.resolve(3);
Promise.all([true, promise])
  .then(function (values) {
    console.log(values);
  });

Promise.race(iterable)

参数是一个iterable对象,如数组。

Promise.race()返回一个promise,当iterable对象中任何一个promise resolve或者reject时,这个promise以这个值进行resolve或者reject。

var p1 = new Promise(function (resolve, reject) {
  setTimeout(resolve, 500, 'one');
});
var p2 = new Promise(function (resolve, reject) {
  setTimeout(resolve, 100, 'two');
});

Promise.race([p1, p2])
  .then(function (value) {
    console.log(value);
  });

var p3 = new Promise(function (resolve, reject) {
  setTimeout(resolve, 100, 'Three');
});
var p4 = new Promise(function (resolve, reject) {
  setTimeout(reject, 500, 'four');
});
Promise.race([p3, p4])
  .then(function (value) {
    console.log(value);
  }, function (reson) {
    // not called
  });

var p5 = new Promise(function (resolve, reject) {
  setTimeout(resolve, 500, 'five');
});
var p6 = new Promise(function (resolve, reject) {
  setTimeout(resolve, 100, 'six');
});
Promise.race([p5, p6])
  .then(function (value) {
    // not called
  }, function (reason) {
    console.log(reason);  // six
  });

Promise.reject(reason)

返回一个以reason为原因的rejected的promise

Promise.reject('Testing static reject')
  .then(function (reason) {
    // not called
  }, function (reason) {
    console.log(reason);  // Testing static reject
  });

Promise.reject(new Error('fail'))
  .then(function (error) {
    // not called
  }, function (error) {
    console.log(error); // stacktrace
  });

Promise.resolve(value)

Promise.resolve()返回一个以value resolved的promise,如果value是thenable(有then方法)。系统会执行then方法,然后根据then方法调用的resovle和reject设置返回的promise状态。

可以这么理解:

// resolve a value
Promise.resolve('Success')
  .then(function (value) {
    console.log(value); // success
  }, function (value) {
    // not called
  });



// resolve an array
Promise.resolve([1, 2, 3])
  .then(function (arr) {
    console.log(arr[0]);  // 1
  });


// resolve another promise
vr original = Promise.resolve(true);
var cast = Promise.resolve(original);
cast.then(function (v) {
  console.log(v); // true
});


// resolve thenable object
var thenable = {
  // 传入对象的then方法执行操作,会有两种结果
  // then执行之后设置的状态就是返回的promise的状态
  then: function (resolve, reject) {
    var r = Math.random();
    if (r > 0.5) {
      resolve(r);
    }
    else {
      reject(r);
    }
  }
};

var p1 = Promise.resolve(thenable);
// 上面相当于以then方法new一个Promise
var p2 = new Promise(thenable.then);

p1.then(function (value) {
  console.log('resolve with: ' + value);
}, function (reason) {
  console.log('reject with: ' + reason)
});

// resolving thenables and throwing errors
var p1 = Promise.resolve({
  then: function (onFulfill, onReject) {
    onFulfill('fulfilled');
  }
});
console.log(p1 instanceof Promise); // true, object casted to a Promise

p1.then(function (v) {
  console.log(v); // fulfilled
}, function (e) {
  // not called
});


// thenable throws before callback
// Promise rejects
var thenable = {
  then: function (resolve) {
    throw new TypeError('Throwing');
    resolve('Resolving');
  }
};

Promise.resolve(thenable)
  .then(function (value) {
    // not called
  }, function (e) {
    console.log(e); // TypeError: Throwing
  });

// thenable throws after callbck
// Promise resolves
var thenable = {
  then: function (resolve) {
    resolve('resolving');
    throw new TypeError('Throwing');
  }
};

Promise.resolve(thenable)
  .then(function (value) {
    console.log(value); // resolving
  }, function (e) {
    // not called
  });

Promise.ptototype.then(onResolve, onReject)

then会返回一个新的promise,promise信息如下

注意

// 成功返回普通值
Promise.resolve(3)
  .then(function (d) {
    console.log('then1: ' + d); // 3
    return d + 1;
  };
  })
  .then(function (d) {
    console.log('then2: ' + d); // 4
    return d + 1;
  })
  .then(function (d) {
    console.log('then3:' + d);  // 5
  })
  .then(function (d) {
    console.log('then4: ' + d); // undefined
  }, function (e) {
    console.log('then4 e:' + e);  // not called
  });

// 返回的是thenable对象
Promise.resolve(0.5)
  .then(function (d) {
    return {
      then: function (resolve, reject) {
        var r = Math.random();
        if (r > d) {
          resolve(r);
        }
        else {
          reject(r);
        }
      }
    }
  }, function (d) {
    return {
      then: function (resolve, reject) {
        var r = Math.random();
        if (r > d) {
          resolve(r);
        }
        else {
          reject(r);
        }
      }
    }
  })
  .then(function (d) {
    console.log('onFulfill2: ' + d);
  }, function (e) {
    console.log('onReject2: ' + e);
  });

Promise.prototype.catch(onReject)

相当于:Promise.prototype.then(undefined, onReject)

Promise.resolve({
  then: function (resolve, reject) {
    var r = Math.random();

    if (r > 0.5) {
      resolve(r);
    }
    else {
      reject(r);
    }
  }
})
.then(function (d) {
  console.log('success: ' + d);
})
.catch(function (e) {
  console.log('error: ' + e);
});

案例

需求:

  1. 显示一个加载指示图标
  2. 加载一个JSON,包含小说名和每一章的URL
  3. 再页面显示小说名
  4. 加载所有章节正文
  5. 再页面中添加章节正文
  6. 停止加载指示

如果发生错误需要提示用户,并去掉加载图标

demo

参考资料