@CSS魔法
2016. 04. 22.
(入门篇)
(30~45 分钟)
Promise 就是 Promise 呀!!
Promise 就是对未来的 “承诺” 呀!
Promise 是一种异步编程方式。
先看最基本的 API。
// 有一个 Promise
var myPromise = (/* 通过某些黑魔法创建了一个 Promise */)
// 刚刚创建的 Promise 就像一个装了礼物的盒子,现在还不能拆
// 但在未来的某个时间点,盒子会自己打开
// 到时候你就知道里面装的是什么了
// 但你现在能做的,只是先想好到时候你要做什么
myPromise.then(function (content) {
// 通过这个 “回调函数” 来约定你要对盒子里的内容做什么
console.log(content)
})
// 有一个 Promise
var myPromise = {/* 通过某些黑魔法创建了一个 Promise */}
// 还有些时候,我们只关心盒子什么时候打开
// 并不关心盒子里有什么东西
myPromise.then(function () {
// 通过这个 “回调函数” 来约定当盒子打开时你要做的事儿
console.log('It is time.')
})
在 JavaScript 的世界里,外部环境如果要通知你某个状态在未来发生了变化,有哪些做法?
以 DOM Ready 为例吧……
// Whatwg API
document.addEventListener('DOMContentLoaded', function () {
/* init widgets here */
});
// 万能的 jQuery
var $ = require('jquery')
$(function () {
/* init widgets here */
});
// 假设有一个类库,它返回一个 Promise
// 这个 Promise 在 DOM Ready 时打开……
var promiseDomReady = require('promise-dom-ready')
promiseDomReady.then(function () {
/* init widgets here */
});
貌似并没有什么卵用?
有一个值,现在还不存在,在等到未来某个时间点才能拿到。在 JavaScript 世界中,如何实现?
比如,我们需要获取用户输入的某个值……
button.submit
请输入一个值:
#dialog
input.text
// 先想好拿到值后要做什么
function handleUserInput(value) {
console.log(value)
}
// 监听用户行为,等待用户提供值
$('#dialog button.submit').once('click', function () {
$('#dialog').close()
var value = $('#dialog input.text').val()
// 当我们需要值出现时,让预先定义好的回调函数来处理它
handleUserInput(value)
});
$('#dialog').popup()
“用值” 的代码混在 “等值” 的代码中了
// 监听用户行为,等待用户提供值
$('#dialog button.submit').once('click', function () {
$('#dialog').close()
var value = $('#dialog input.text').val()
// 通过自定义事件把值抛出
$(window).trigger('user-input', {value: value})
});
$('#dialog').popup()
// 监听这个自定义事件,以便在未来获取抛出的值
$(window).on('user-input', function (event, param) {
console.log(param.value)
})
“等值” 的代码和 “用值” 的代码分得比较清楚了。
// 把用户在未来提供的值包装成一个 Promise
var promiseUserInput = {/* 通过某些黑魔法创建了一个 Promise */}
// 等值可用时,我们来处理它
promiseUserInput.then(function (value) {
console.log(value)
})
很清爽,有木有?
但是……“黑魔法” 部分属于作弊吧?
对应用层来说,连对话框的弹出和关闭都不需要操心了。
第二个重要的 API:
如何创建一个 Promise?
// Promise 构造器
var myPromise = new Promise(function (resolve, reject) {
// 我们在这里决定这个 Promise 如何、何时得到未来的值
// 再用 resolve() 函数来把值抛出来
resolve(...)
})
// Promise 构造器
var myPromise = new Promise(function (resolve, reject) {
// 我们在这里决定这个 Promise 如何、何时得到未来的值
// 再用 resolve() 函数来把值抛出来
// 不过一般来说,resolve() 都不会是同步调用
// 它往往在一个异步过程中被调用,比如:
setTimeout(function () {
resolve(...)
}, 1000)
})
我们来把那个 “等待用户输入”
的例子写完整。
// 把用户在未来提供的值包装成一个 Promise
var promiseUserInput = {/* 通过某些黑魔法创建了一个 Promise */}
// 等值可用时,我们来处理它
promiseUserInput.then(function (value) {
console.log(value)
})
// 把用户在未来提供的值包装成一个 Promise
var promiseUserInput = new Promise(function (resolve, reject) {
$('#dialog').popup()
$('#dialog button.submit').once('click', function () {
$('#dialog').close()
var value = $('#dialog input.text').val()
// 等值出现时,把它抛出
resolve(value)
});
})
我差不多懂了,我们找个案例来试试 Promise 吧!
// 以前设计的 API 一般采用回调函数方式
XStorage.getItem = function (key, callback) {
/* ... */
}
XStorage.setItem = function (key, value, callback) {
/* ... */
}
它用于跨域存取本地存储。
它通过 postMessage()
来实现跨域通讯,因此天生是异步的。
能用,但不好看,也不好用。
// 所有 API 返回 Promise
XStorage.getItem = function (key) {
return new Promise(function (resolve, reject) {
/* ... */
})
}
XStorage.setItem = function (key, value) {
return new Promise(function (resolve, reject) {
/* ... */
})
}
// 在调用时
XStorage.getItem('key').then(...)
XStorage.setItem('key', 1).then(...)
这才是 Promise 真正的威力所在。
Task 1
Task 2
Task 3
Task 4
// 我们有一些异步任务,每个任务完成后会调用传给它的回调函数
function asyncTask1(callback) {...}
function asyncTask2(callback) {...}
function asyncTask3(callback) {...}
function asyncTask4(callback) {...}
// 通过回调函数来实现顺序依赖
asyncTask1(function () {
asyncTask2(function () {
asyncTask3(function () {
asyncTask4(function () {
console.log('All done!')
})
})
})
})
传说中的 “回调金字塔”!
关于 .then()
这个 API 的真相……
每次调用 .then()
都返回一个新的 Promise
// 有一个 Promise
var myPromise = new Promise(...)
myPromise
.then(...) // 返回一个新的 Promise
.then(...) // 又返回一个新的 Promise
//...
可以一直 .then()
下去!
“链式调用”!
(如果其返回值是 Promise,则 .then()
将其直接返回。)
每个 Promise 都可以包装一个值,
这个新的 Promise 包装的值从哪里来?
.then()
的回调函数的返回值
将被包装在这个新 Promise 中
myPromise.then(function () {
/* 做了一些事 */
return something
})
// 有一个 Promise
var myPromise = new Promise(...)
myPromise
.then(function (value) {
return handle(value)
}) // 返回一个 Promise,它包装的值是上一个 Promise 包装的值处理后的结果
.then(function (value) {
var promiseAjax = new Promise (function (resolve) {
$.post(url, {qty: value}, function (data) {
resolve(data)
})
})
return promiseAjax
}) // 直接返回 promiseAjax
// 我们有一些异步任务,每个任务均返回一个 Promise
function asyncTask1() {...}
function asyncTask2() {...}
function asyncTask3() {...}
function asyncTask4() {...}
// 通过 .then() 来实现顺序依赖
asyncTask1().then(function () {
return asyncTask2()
}).then(function () {
return asyncTask3()
}).then(function () {
return asyncTask4()
}).then(function () {
console.log('All done.')
})
还可以简化!
// 我们有一些异步任务,每个任务均返回一个 Promise
function asyncTask1() {...}
function asyncTask2() {...}
function asyncTask3() {...}
function asyncTask4() {...}
// 通过 .then() 来实现顺序依赖
asyncTask1()
.then(asyncTask2)
.then(asyncTask3)
.then(asyncTask4)
.then(function () {
console.log('All done.')
})
Task 1
Task 2
Task 3
Task 4
// 这个 API 可以监控多个 Promise 的状态
// 它接受多个 Promise 组成的数组,返回一个 Promise
// 当所有 Promise 都完成后,它自己也会完成。
Promise.all([
promise1,
promise2,
...
])
Promise.all()
// 我们有一些异步任务,每个任务均返回一个 Promise
function asyncTask1() {...}
function asyncTask2() {...}
function asyncTask3() {...}
function asyncTask4() {...}
// 通过 Promise.all() 来监控多个并行任务何时全部完成
Promise.all([
asyncTask1(),
asyncTask2(),
asyncTask3(),
asyncTask4(),
])
.then(function () {
console.log('All done.')
})
Task 1
Task 2
Task 3
Task 4
// 这个 API 可以监控多个 Promise 的状态
// 它接受多个 Promise 组成的数组,返回一个 Promise
// 当其中有一个 Promise 完成时,它自己也会完成。
Promise.race([
promise1,
promise2,
...
])
Promise.race()
// 我们有一些异步任务,每个任务均返回一个 Promise
function asyncTask1() {...}
function asyncTask2() {...}
function asyncTask3() {...}
function asyncTask4() {...}
// 通过 Promise.race() 来监控多个并行任务中谁最先完成
Promise.race([
asyncTask1(),
asyncTask2(),
asyncTask3(),
asyncTask4(),
])
.then(function () {
console.log('At less one task done.')
})
Thank You!