實戰(zhàn)|拼豆制圖45:上傳生成鏈路如何用狀態(tài)機替代散落字符串)
上傳生成看起來只有兩個按鈕選擇圖片、生成圖紙。真正運行時卻至少包含空閑、打開圖庫、已選擇、轉(zhuǎn)換中、成功和失敗六種狀態(tài)。若只依靠pickedImageUri、一條createStatus字符串和若干布爾值拼判斷很容易出現(xiàn)舊預(yù)覽沒有清空、轉(zhuǎn)換中還能重復(fù)點擊、錯誤后按鈕文案與數(shù)據(jù)不一致。這篇文章把《拼豆制圖》的創(chuàng)建頁改造成一個顯式狀態(tài)機讓 Harmony os 頁面渲染、按鈕可用性和異步回調(diào)都由同一個狀態(tài)事實源驅(qū)動。一、先列出真實狀態(tài)而不是控件狀態(tài)exporttypeCreatePhaseidle|picking|picked|converting|ready|failed;exportinterfaceCreateState{phase:CreatePhase;imageUri:string;result:Pattern|null;message:string;requestId:number;}requestId用來識別過期異步結(jié)果。它不是展示字段卻是防止“后發(fā)請求先完成”覆蓋當(dāng)前選擇的關(guān)鍵。二、定義允許發(fā)生的狀態(tài)遷移當(dāng)前狀態(tài)事件下一狀態(tài)idle點擊選擇pickingpicking選擇成功pickedpicking取消idlepicked點擊生成convertingconverting轉(zhuǎn)換成功readyconverting轉(zhuǎn)換失敗failedready / failed重新選擇picking未列出的遷移應(yīng)被拒絕。例如converting期間再次點擊生成不應(yīng)該再創(chuàng)建第二個任務(wù)。三、用一個State保存完整快照StatecreateState:CreateState{phase:idle,imageUri:,result:null,message:請選擇一張圖片,requestId:0};privatereplaceCreateState(next:CreateState):void{this.createStatenext;}整對象替換能讓 ArkUI 明確收到狀態(tài)變化也避免只改了消息、忘記同步 URI 或結(jié)果。四、選擇圖片時區(qū)分取消與失敗privateasyncpickImage():Promisevoid{if(this.createState.phasepicking||this.createState.phaseconverting){return;}constrequestIdthis.createState.requestId1;this.replaceCreateState({phase:picking,imageUri:,result:null,message:正在打開系統(tǒng)圖庫...,requestId});try{consturiawaitImagePickerService.pickSingleImage();if(requestId!this.createState.requestId){return;}if(uri.length0){this.replaceCreateState({phase:idle,imageUri:,result:null,message:未選擇圖片,requestId});return;}this.replaceCreateState({phase:picked,imageUri:uri,result:null,message:圖片已選擇可以生成編號圖,requestId});}catch(error){this.failCreate(requestId,errorasError);}}取消不是失敗頁面不應(yīng)顯示紅色錯誤也不應(yīng)保留上一次圖片冒充新選擇。五、生成任務(wù)要拒絕過期結(jié)果privateasyncconvertPickedImage():Promisevoid{if(this.createState.phase!picked){return;}constrequestIdthis.createState.requestId1;consturithis.createState.imageUri;this.replaceCreateState({phase:converting,imageUri:uri,result:null,message:正在讀取像素并生成 70×70 圖紙...,requestId});try{constoutputawaitImageConvertService.convertFromPickedImage(uri,this.getSelectedPattern());if(requestId!this.createState.requestId){return;}this.replaceCreateState({phase:ready,imageUri:uri,result:output.pattern,message:output.message,requestId});}catch(error){this.failCreate(requestId,errorasError);}}即使用戶快速重新選擇舊任務(wù)完成后也無法覆蓋新任務(wù)因為它攜帶的requestId已過期。六、按鈕文案與可用性由狀態(tài)派生privatecreateButtonText():string{if(this.createState.phasepicking){return打開圖庫中...;}if(this.createState.phaseconverting){return生成中...;}if(this.createState.phaseready){return重新生成;}return生成圖紙;}privatecanConvert():boolean{returnthis.createState.phasepicked;}頁面不再解析消息文本來判斷業(yè)務(wù)狀態(tài)。消息只負責(zé)展示phase才負責(zé)決策。七、失敗狀態(tài)保留可恢復(fù)輸入privatefailCreate(requestId:number,error:Error):void{if(requestId!this.createState.requestId){return;}this.replaceCreateState({phase:failed,imageUri:this.createState.imageUri,result:null,message:error.message.length0?error.message:生成失敗請重新選擇圖片,requestId});}是否允許直接重試要根據(jù)錯誤類型決定。若 URI 仍可讀可以提供“重試”若訪問失效則引導(dǎo)重新選擇不要一律清空上下文。八、成功后再提交副作用生成結(jié)果、最近記錄、當(dāng)前圖紙和頁面跳轉(zhuǎn)應(yīng)按固定順序提交privatecommitGeneratedPattern(pattern:Pattern):void{this.generatedPatternpattern;this.selectedPatternIdpattern.id;this.savePatternRecord(pattern);this.exportStatus;this.activeTabnumbered;}不要在轉(zhuǎn)換開始前寫最近記錄也不要在結(jié)果尚未通過數(shù)據(jù)不變量校驗時跳到編號圖頁。九、狀態(tài)機測試比截圖更能抓住競態(tài)建議覆蓋轉(zhuǎn)換中第二次點擊不會發(fā)起服務(wù)調(diào)用。取消選擇后imageUri為空。舊requestId的成功回調(diào)不會覆蓋新狀態(tài)。失敗后按鈕可以恢復(fù)到重新選擇路徑。成功后才寫入最近記錄。十、常見問題與修復(fù)問題表現(xiàn)修復(fù)用狀態(tài)文案做判斷改文案后邏輯失效使用窄聯(lián)合phase轉(zhuǎn)換中可重復(fù)點擊多任務(wù)互相覆蓋入口狀態(tài)守衛(wèi)重新選擇仍顯示舊結(jié)果結(jié)果字段未清空進入 picking 時重置結(jié)果舊任務(wù)晚到覆蓋新圖無請求身份使用遞增requestId復(fù)現(xiàn)競態(tài)時不要只快速點擊按鈕還要人為控制兩個異步任務(wù)的完成順序。讓舊請求最后返回再確認它因為requestId不一致而被丟棄這種反序用例可以證明狀態(tài)機真正處理了晚到結(jié)果而不只是依賴設(shè)備當(dāng)前恰好較快。十一、驗證清單每個界面狀態(tài)都能映射到唯一phase。未列出的遷移不會執(zhí)行。取消、失敗、成功使用不同語義。重復(fù)點擊不會啟動并發(fā)轉(zhuǎn)換。重新選擇后舊回調(diào)被忽略。記錄與跳轉(zhuǎn)只在成功提交時發(fā)生。每項驗證都應(yīng)同時觀察頁面狀態(tài)和副作用調(diào)用次數(shù)。例如失敗后不僅要看到可重試文案還要確認最近記錄沒有增加、當(dāng)前圖紙沒有替換成功后則應(yīng)保證記錄只寫一次避免界面正確但存儲已經(jīng)重復(fù)。十二、總結(jié)上傳生成鏈路的難點不在按鈕數(shù)量而在異步事件的先后關(guān)系。用一個類型化狀態(tài)快照承載階段、輸入、結(jié)果和請求身份Harmony os 創(chuàng)建頁就能把重復(fù)點擊、過期回調(diào)和錯誤恢復(fù)收口到可驗證的遷移規(guī)則里。標簽Harmony os、ArkTS、狀態(tài)機、異步任務(wù)、圖片生成